Make transaction-rollback=true the default for test-suite (#1663)

Change test-suite to use db-tx-rollback-all = true by default
This commit is contained in:
Wolfgang Walther
2020-12-03 18:54:33 +01:00
committed by GitHub
parent 10a70d4e52
commit 609c9aead8
21 changed files with 708 additions and 495 deletions
+30 -15
View File
@@ -33,13 +33,24 @@ spec actualPgVersion =
{"id": 1, "child_entities": [ { "id": 1 }, { "id": 2 } ] }, { "id": 2, "child_entities": []},
{"id": 3, "child_entities": []}, {"id": 4, "child_entities": []}
]|] { matchHeaders = [matchContentTypeJson] }
it "can do logic on the third level" $
get "/entities?child_entities.grandchild_entities.or=(id.eq.1,id.eq.2)&select=id,child_entities(id,grandchild_entities(id))" `shouldRespondWith`
[json|[
{"id": 1, "child_entities": [ { "id": 1, "grandchild_entities": [ { "id": 1 }, { "id": 2 } ]}, { "id": 2, "grandchild_entities": []}]},
{"id": 2, "child_entities": [ { "id": 3, "grandchild_entities": []} ]},
{"id": 3, "child_entities": []}, {"id": 4, "child_entities": []}
]|] { matchHeaders = [matchContentTypeJson] }
get "/entities?child_entities.grandchild_entities.or=(id.eq.1,id.eq.2)&select=id,child_entities(id,grandchild_entities(id))"
`shouldRespondWith`
[json|[
{"id": 1, "child_entities": [
{ "id": 1, "grandchild_entities": [ { "id": 1 }, { "id": 2 } ]},
{ "id": 2, "grandchild_entities": []},
{ "id": 4, "grandchild_entities": []},
{ "id": 5, "grandchild_entities": []}
]},
{"id": 2, "child_entities": [
{ "id": 3, "grandchild_entities": []},
{ "id": 6, "grandchild_entities": []}
]},
{"id": 3, "child_entities": []},
{"id": 4, "child_entities": []}
]|]
context "and/or params combined" $ do
it "can be nested inside the same expression" $
@@ -210,12 +221,15 @@ spec actualPgVersion =
context "used with POST" $
it "includes related data with filters" $
request methodPost "/child_entities?select=id,entities(id)&entities.or=(id.eq.2,id.eq.3)&entities.order=id"
[("Prefer", "return=representation")]
[json|[{"id":4,"name":"entity 4","parent_id":1},
{"id":5,"name":"entity 5","parent_id":2},
{"id":6,"name":"entity 6","parent_id":3}]|] `shouldRespondWith`
[json|[{"id": 4, "entities":null}, {"id": 5, "entities": {"id": 2}}, {"id": 6, "entities": {"id": 3}}]|]
{ matchStatus = 201, matchHeaders = [matchContentTypeJson] }
[("Prefer", "return=representation")]
[json|[
{"id":7,"name":"entity 4","parent_id":1},
{"id":8,"name":"entity 5","parent_id":2},
{"id":9,"name":"entity 6","parent_id":3}
]|]
`shouldRespondWith`
[json|[{"id": 7, "entities":null}, {"id": 8, "entities": {"id": 2}}, {"id": 9, "entities": {"id": 3}}]|]
{ matchStatus = 201 }
context "used with PATCH" $
it "succeeds when using and/or params" $
@@ -228,9 +242,10 @@ spec actualPgVersion =
context "used with DELETE" $
it "succeeds when using and/or params" $
request methodDelete "/grandchild_entities?or=(id.eq.1,id.eq.2)&select=id,name"
[("Prefer", "return=representation")] "" `shouldRespondWith`
[json|[{ "id": 1, "name" : "updated grandchild entity"},{ "id": 2, "name" : "updated grandchild entity"}]|]
{ matchHeaders = [matchContentTypeJson] }
[("Prefer", "return=representation")]
""
`shouldRespondWith`
[json|[{ "id": 1, "name" : "grandchild entity 1" },{ "id": 2, "name" : "grandchild entity 2" }]|]
it "can query columns that begin with and/or reserved words" $
get "/grandchild_entities?or=(and_starting_col.eq.smth, or_starting_col.eq.smth)" `shouldRespondWith` 200
-8
View File
@@ -57,14 +57,6 @@ spec =
, matchHeaders = ["Content-Range" <:> "*/*"]
}
it "actually clears items ouf the db" $ do
_ <- request methodDelete "/items?id=lt.15" [] ""
get "/items"
`shouldRespondWith` [json|[{"id":15}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-0/*"]
}
context "known route, no records matched" $
it "includes [] body if return=rep" $
request methodDelete "/items?id=eq.101"
+3 -3
View File
@@ -194,9 +194,9 @@ spec =
{ matchHeaders = [matchContentTypeJson] }
it "can request two parents with fks" $
get "/articleStars?select=createdAt,article(owner),user(name)&limit=1" `shouldRespondWith`
[json|[{"createdAt":"2015-12-08T04:22:57.472738","article":{"owner": "postgrest_test_authenticator"},"user":{"name": "Angela Martin"}}]|]
{ matchHeaders = [matchContentTypeJson] }
get "/articleStars?select=createdAt,article(id),user(name)&limit=1"
`shouldRespondWith`
[json|[{"createdAt":"2015-12-08T04:22:57.472738","article":{"id": 1},"user":{"name": "Angela Martin"}}]|]
it "can specify a view!fk" $
get "/message?select=id,body,sender:person_detail!message_sender_fkey(name,sent),recipient:person_detail!message_recipient_fkey(name,received)&id=lt.4" `shouldRespondWith`
+53 -36
View File
@@ -135,18 +135,21 @@ spec actualPgVersion = do
context "with no pk supplied" $ do
context "into a table with auto-incrementing pk" $
it "succeeds with 201 and link" $ do
p <- post "/auto_incrementing_pk" [json| { "non_nullable_string":"not null"} |]
liftIO $ do
simpleBody p `shouldBe` ""
simpleHeaders p `shouldSatisfy` matchHeader hLocation "/auto_incrementing_pk\\?id=eq\\.[0-9]+"
simpleStatus p `shouldBe` created201
let Just location = lookup hLocation $ simpleHeaders p
r <- get location
let [record] = fromJust (JSON.decode $ simpleBody r :: Maybe [IncPK])
liftIO $ do
incStr record `shouldBe` "not null"
incNullableStr record `shouldBe` Nothing
it "succeeds with 201 and location header" $ do
-- reset pk sequence first to make test repeatable
request methodPost "/rpc/reset_sequence"
[("Prefer", "tx=commit")]
[json|{"name": "auto_incrementing_pk_id_seq", "value": 2}|]
`shouldRespondWith`
[json|""|]
post "/auto_incrementing_pk"
[json| { "non_nullable_string":"not null"} |]
`shouldRespondWith`
""
{ matchStatus = 201
, matchHeaders = [ "Location" <:> "/auto_incrementing_pk?id=eq.2" ]
}
context "into a table with simple pk" $
it "fails with 400 and error" $
@@ -197,21 +200,14 @@ spec actualPgVersion = do
context "with compound pk supplied" $
it "builds response location header appropriately" $ do
let inserted = [json| { "k1":12, "k2":"Rock & R+ll" } |]
expectedObj = CompoundPK 12 "Rock & R+ll" Nothing
expectedLoc = "/compound_pk?k1=eq.12&k2=eq.Rock%20%26%20R%2Bll"
p <- request methodPost "/compound_pk"
[("Prefer", "return=representation")]
inserted
liftIO $ do
JSON.decode (simpleBody p) `shouldBe` Just [expectedObj]
simpleStatus p `shouldBe` created201
lookup hLocation (simpleHeaders p) `shouldBe` Just expectedLoc
r <- get expectedLoc
liftIO $ do
JSON.decode (simpleBody r) `shouldBe` Just [expectedObj]
simpleStatus r `shouldBe` ok200
request methodPost "/compound_pk"
[("Prefer", "return=representation")]
[json| { "k1":12, "k2":"Rock & R+ll" } |]
`shouldRespondWith`
[json|[ { "k1":12, "k2":"Rock & R+ll", "extra": null } ]|]
{ matchStatus = 201
, matchHeaders = [ "Location" <:> "/compound_pk?k1=eq.12&k2=eq.Rock%20%26%20R%2Bll" ]
}
context "with bulk insert" $
it "returns 201 but no location header" $ do
@@ -248,9 +244,9 @@ spec actualPgVersion = do
context "attempting to insert a row with the same primary key" $
it "fails returning a 409 Conflict" $
post "/simple_pk"
[json| { "k":"k1", "extra":"e1" } |]
[json| { "k":"xyyx", "extra":"e1" } |]
`shouldRespondWith`
[json|{"hint":null,"details":"Key (k)=(k1) already exists.","code":"23505","message":"duplicate key value violates unique constraint \"simple_pk_pkey\""}|]
[json|{"hint":null,"details":"Key (k)=(xyyx) already exists.","code":"23505","message":"duplicate key value violates unique constraint \"simple_pk_pkey\""}|]
{ matchStatus = 409 }
context "attempting to insert a row with conflicting unique constraint" $
@@ -293,16 +289,30 @@ spec actualPgVersion = do
, matchHeaders = []
}
it "successfully inserts a row with all-default columns with prefer=rep" $
it "successfully inserts a row with all-default columns with prefer=rep" $ do
-- reset pk sequence first to make test repeatable
request methodPost "/rpc/reset_sequence"
[("Prefer", "tx=commit")]
[json|{"name": "items_id_seq", "value": 20}|]
`shouldRespondWith`
[json|""|]
request methodPost "/items" [("Prefer", "return=representation")] "{}"
`shouldRespondWith` [json|[{ id: 20 }]|]
{ matchStatus = 201,
matchHeaders = []
}
it "successfully inserts a row with all-default columns with prefer=rep and &select=" $
it "successfully inserts a row with all-default columns with prefer=rep and &select=" $ do
-- reset pk sequence first to make test repeatable
request methodPost "/rpc/reset_sequence"
[("Prefer", "tx=commit")]
[json|{"name": "items_id_seq", "value": 20}|]
`shouldRespondWith`
[json|""|]
request methodPost "/items?select=id" [("Prefer", "return=representation")] "{}"
`shouldRespondWith` [json|[{ id: 21 }]|]
`shouldRespondWith` [json|[{ id: 20 }]|]
{ matchStatus = 201,
matchHeaders = []
}
@@ -411,15 +421,22 @@ spec actualPgVersion = do
it "succeeds and returns usable location header" $ do
let payload = [json| { "k":"圍棋", "extra":"" } |]
p <- request methodPost "/simple_pk?select=extra,k"
[("Prefer", "return=representation")]
payload
[("Prefer", "tx=commit"), ("Prefer", "return=representation")]
payload
liftIO $ do
simpleBody p `shouldBe` "["<>payload<>"]"
simpleStatus p `shouldBe` created201
let Just location = lookup hLocation $ simpleHeaders p
r <- get (location <> "&select=extra,k")
liftIO $ simpleBody r `shouldBe` "["<>payload<>"]"
get location
`shouldRespondWith`
[json|[ { "k":"圍棋", "extra":"" } ]|]
request methodDelete location
[("Prefer", "tx=commit")]
""
`shouldRespondWith`
204
describe "Row level permission" $
it "set user_id when inserting rows" $ do
+5 -6
View File
@@ -202,12 +202,11 @@ spec actualPgVersion = describe "json and jsonb operators" $ do
context "Patching record, in a nonempty table" $
it "can set a json column to escaped value" $ do
_ <- post "/json_table" [json| { data: {"escaped":"bar"} } |]
request methodPatch "/json_table?data->>escaped=eq.bar"
[("Prefer", "return=representation")]
[json| { "data": { "escaped":" \"bar" } } |]
`shouldRespondWith` [json| [{ "data": { "escaped":" \"bar" } }] |]
{ matchStatus = 200 , matchHeaders = [] }
request methodPatch "/json_table?data->>id=eq.3"
[("Prefer", "return=representation")]
[json| { "data": { "id":" \"escaped" } } |]
`shouldRespondWith`
[json| [{ "data": { "id":" \"escaped" } }] |]
when (actualPgVersion >= pgVersion95) $
context "json array negative index" $ do
+31 -33
View File
@@ -77,33 +77,37 @@ spec actualPgVersion =
context "Inserting tables on different schemas" $ do
it "succeeds inserting on default schema and returning it" $
request methodPost "/children" [("Prefer", "return=representation")] [json|{"name": "child v1-1", "parent_id": 1}|]
`shouldRespondWith`
[json|[{"id":1, "name": "child v1-1", "parent_id": 1}]|]
{
matchStatus = 201
, matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v1"]
}
request methodPost "/children"
[("Prefer", "return=representation")]
[json|{"id": 0, "name": "child v1-1", "parent_id": 1}|]
`shouldRespondWith`
[json|[{"id": 0, "name": "child v1-1", "parent_id": 1}]|]
{
matchStatus = 201
, matchHeaders = ["Content-Profile" <:> "v1"]
}
it "succeeds inserting on the v1 schema and returning its parent" $
request methodPost "/children?select=id,parent(*)" [("Prefer", "return=representation"), ("Content-Profile", "v1")]
[json|{"name": "child v1-2", "parent_id": 2}|]
request methodPost "/children?select=id,parent(*)"
[("Prefer", "return=representation"), ("Content-Profile", "v1")]
[json|{"id": 0, "name": "child v1-2", "parent_id": 2}|]
`shouldRespondWith`
[json|[{"id":2, "parent": {"id": 2, "name": "parent v1-2"}}]|]
{
matchStatus = 201
, matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v1"]
}
[json|[{"id": 0, "parent": {"id": 2, "name": "parent v1-2"}}]|]
{
matchStatus = 201
, matchHeaders = ["Content-Profile" <:> "v1"]
}
it "succeeds inserting on the v2 schema and returning its parent" $
request methodPost "/children?select=id,parent(*)" [("Prefer", "return=representation"), ("Content-Profile", "v2")]
[json|{"name": "child v2-3", "parent_id": 3}|]
request methodPost "/children?select=id,parent(*)"
[("Prefer", "return=representation"), ("Content-Profile", "v2")]
[json|{"id": 0, "name": "child v2-3", "parent_id": 3}|]
`shouldRespondWith`
[json|[{"id":1, "parent": {"id": 3, "name": "parent v2-3"}}]|]
{
matchStatus = 201
, matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v2"]
}
[json|[{"id": 0, "parent": {"id": 3, "name": "parent v2-3"}}]|]
{
matchStatus = 201
, matchHeaders = ["Content-Profile" <:> "v2"]
}
it "fails when inserting on an unknown schema" $
request methodPost "/children" [("Content-Profile", "unknown")]
@@ -180,18 +184,12 @@ spec actualPgVersion =
}
it "succeeds on deleting on the v2 schema" $ do
request methodDelete "/children?id=eq.1" [("Content-Profile", "v2"), ("Prefer", "return=representation")] ""
`shouldRespondWith` [json|[{"id": 1, "name": "child v2-1 updated", "parent_id": 3}]|]
{
matchStatus = 200
, matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v2"]
}
request methodGet "/children?id=eq.1" [("Accept-Profile", "v2")] ""
`shouldRespondWith` "[]"
{
matchStatus = 200
, matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v2"]
}
request methodDelete "/children?id=eq.1"
[("Content-Profile", "v2"), ("Prefer", "return=representation")]
""
`shouldRespondWith`
[json|[{"id": 1, "name": "child v2-3", "parent_id": 3}]|]
{ matchHeaders = ["Content-Profile" <:> "v2"] }
when (actualPgVersion >= pgVersion96) $
it "succeeds on PUT on the v2 schema" $
+32 -35
View File
@@ -15,59 +15,56 @@ spec :: SpecWith ((), Application)
spec =
describe "Requesting many items with server limits(max-rows) enabled" $ do
it "restricts results" $
get "/items"
`shouldRespondWith` [json| [{"id":1},{"id":2}] |]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-1/*"]
}
get "/items?order=id"
`shouldRespondWith`
[json| [{"id":1},{"id":2}] |]
{ matchHeaders = ["Content-Range" <:> "0-1/*"] }
it "respects additional client limiting" $ do
r <- request methodGet "/items"
(rangeHdrs $ ByteRangeFromTo 0 0) ""
liftIO $ do
simpleHeaders r `shouldSatisfy`
matchHeader "Content-Range" "0-0/*"
simpleStatus r `shouldBe` ok200
request methodGet "/items"
(rangeHdrs $ ByteRangeFromTo 0 0)
""
`shouldRespondWith`
[json| [{"id":1}] |]
{ matchHeaders = ["Content-Range" <:> "0-0/*"] }
it "works on all levels" $
get "/users?select=id,tasks(id)&order=id.asc&tasks.order=id.asc"
`shouldRespondWith` [json|[{"id":1,"tasks":[{"id":1},{"id":2}]},{"id":2,"tasks":[{"id":5},{"id":6}]}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-1/*"]
}
`shouldRespondWith`
[json|[{"id":1,"tasks":[{"id":1},{"id":2}]},{"id":2,"tasks":[{"id":5},{"id":6}]}]|]
{ matchHeaders = ["Content-Range" <:> "0-1/*"] }
it "succeeds in getting parent embeds despite the limit, see #647" $
get "/tasks?select=id,project:projects(id)&id=gt.5"
`shouldRespondWith` [json|[{"id":6,"project":{"id":3}},{"id":7,"project":{"id":4}}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-1/*"]
}
`shouldRespondWith`
[json|[{"id":6,"project":{"id":3}},{"id":7,"project":{"id":4}}]|]
{ matchHeaders = ["Content-Range" <:> "0-1/*"] }
it "can offset the parent embed, being consistent with the other embed types" $
get "/tasks?select=id,project:projects(id)&id=gt.5&project.offset=1"
`shouldRespondWith` [json|[{"id":6,"project":null}, {"id":7,"project":null}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-1/*"]
}
`shouldRespondWith`
[json|[{"id":6,"project":null}, {"id":7,"project":null}]|]
{ matchHeaders = ["Content-Range" <:> "0-1/*"] }
context "count=estimated" $ do
it "uses the query planner guess when query rows > maxRows" $
request methodHead "/getallprojects_view" [("Prefer", "count=estimated")] ""
`shouldRespondWith` ""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/2019"]
}
`shouldRespondWith`
""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/2019"]
}
it "gives exact count when query rows <= maxRows" $
request methodHead "/getallprojects_view?id=lt.3" [("Prefer", "count=estimated")] ""
`shouldRespondWith` ""
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-1/2"]
}
`shouldRespondWith`
""
{ matchHeaders = ["Content-Range" <:> "0-1/2"] }
it "only uses the query planner guess if it's indeed greater than the exact count" $
request methodHead "/get_projects_above_view" [("Prefer", "count=estimated")] ""
`shouldRespondWith` ""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/3"]
}
`shouldRespondWith`
""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/3"]
}
+4 -4
View File
@@ -35,7 +35,7 @@ spec actualPgVersion = do
{ matchHeaders = ["Content-Range" <:> "0-0/*"] }
it "matches with equality using not operator" $
get "/items?id=not.eq.5"
get "/items?id=not.eq.5&order=id"
`shouldRespondWith` [json| [{"id":1},{"id":2},{"id":3},{"id":4},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15}] |]
{ matchHeaders = ["Content-Range" <:> "0-13/*"] }
@@ -401,9 +401,9 @@ spec actualPgVersion = do
get "/materialized_projects?select=*,users(*)" `shouldRespondWith` 200
it "can request two parents" $
get "/articleStars?select=createdAt,article:articles(owner),user:users(name)&limit=1" `shouldRespondWith`
[json|[{"createdAt":"2015-12-08T04:22:57.472738","article":{"owner": "postgrest_test_authenticator"},"user":{"name": "Angela Martin"}}]|]
{ matchHeaders = [matchContentTypeJson] }
get "/articleStars?select=createdAt,article:articles(id),user:users(name)&limit=1"
`shouldRespondWith`
[json|[{"createdAt":"2015-12-08T04:22:57.472738","article":{"id": 1},"user":{"name": "Angela Martin"}}]|]
it "can detect relations in views from exposed schema that are based on tables in private schema and have columns renames" $
get "/articles?id=eq.1&select=id,articleStars(users(*))" `shouldRespondWith`
+97 -63
View File
@@ -33,9 +33,11 @@ spec = do
`shouldRespondWith` [json| [] |] {matchHeaders = ["Content-Range" <:> "*/*"]}
it "returns range Content-Range with range/*" $
request methodPost "/rpc/getitemrange" [] defaultRange
`shouldRespondWith` [json| [{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15}] |]
{ matchHeaders = ["Content-Range" <:> "0-14/*"] }
post "/rpc/getitemrange?order=id"
defaultRange
`shouldRespondWith`
[json| [{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15}] |]
{ matchHeaders = ["Content-Range" <:> "0-14/*"] }
context "with range headers" $ do
context "of acceptable range" $ do
@@ -168,20 +170,16 @@ spec = do
}
it "succeeds if offset equals 0 as a no-op" $
get "/items?select=id&offset=0"
get "/items?select=id&offset=0&order=id"
`shouldRespondWith`
[json|[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-14/*"]
}
[json|[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15}]|]
{ matchHeaders = ["Content-Range" <:> "0-14/*"] }
it "succeeds if offset is negative as a no-op" $
get "/items?select=id&offset=-4"
get "/items?select=id&offset=-4&order=id"
`shouldRespondWith`
[json|[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-14/*"]
}
[json|[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15}]|]
{ matchHeaders = ["Content-Range" <:> "0-14/*"] }
it "fails if limit equals 0" $
get "/items?select=id&limit=0"
@@ -199,62 +197,98 @@ spec = do
context "when count=planned" $ do
it "obtains a filtered range" $ do
request methodGet "/items?select=id&id=gt.8" [("Prefer", "count=planned")] ""
`shouldRespondWith` [json|[{"id":9}, {"id":10}, {"id":11}, {"id":12}, {"id":13}, {"id":14}, {"id":15}]|]
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-6/8"]
}
request methodGet "/child_entities?select=id&id=gt.3" [("Prefer", "count=planned")] ""
`shouldRespondWith` [json|[{"id":4}, {"id":5}, {"id":6}]|]
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-2/4"]
}
request methodGet "/getallprojects_view?select=id&id=lt.3" [("Prefer", "count=planned")] ""
`shouldRespondWith` [json|[{"id":1}, {"id":2}]|]
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/673"]
}
request methodGet "/items?select=id&id=gt.8"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
[json|[{"id":9}, {"id":10}, {"id":11}, {"id":12}, {"id":13}, {"id":14}, {"id":15}]|]
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-6/8"]
}
request methodGet "/child_entities?select=id&id=gt.3"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
[json|[{"id":4}, {"id":5}, {"id":6}]|]
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-2/4"]
}
request methodGet "/getallprojects_view?select=id&id=lt.3"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
[json|[{"id":1}, {"id":2}]|]
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/673"]
}
it "obtains the full range" $ do
request methodHead "/items" [("Prefer", "count=planned")] ""
`shouldRespondWith` ""
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-14/15"]
}
request methodHead "/child_entities" [("Prefer", "count=planned")] ""
`shouldRespondWith` ""
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-5/6"]
}
request methodHead "/getallprojects_view" [("Prefer", "count=planned")] ""
`shouldRespondWith` ""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-4/2019"]
}
request methodHead "/items"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
""
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-14/15"]
}
request methodHead "/child_entities"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
""
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-5/6"]
}
request methodHead "/getallprojects_view"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-4/2019"]
}
it "ignores limit/offset on the planned count" $ do
request methodHead "/items?limit=2&offset=3" [("Prefer", "count=planned")] ""
`shouldRespondWith` ""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "3-4/15"]
}
request methodHead "/child_entities?limit=2" [("Prefer", "count=planned")] ""
`shouldRespondWith` ""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/6"]
}
request methodHead "/getallprojects_view?limit=2" [("Prefer", "count=planned")] ""
`shouldRespondWith` ""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/2019"]
}
request methodHead "/items?limit=2&offset=3"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "3-4/15"]
}
request methodHead "/child_entities?limit=2"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/6"]
}
request methodHead "/getallprojects_view?limit=2"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
""
{ matchStatus = 206
, matchHeaders = ["Content-Range" <:> "0-1/2019"]
}
it "works with two levels" $
request methodHead "/child_entities?select=*,entities(*)" [("Prefer", "count=planned")] ""
`shouldRespondWith` ""
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-5/6"]
}
request methodHead "/child_entities?select=*,entities(*)"
[("Prefer", "count=planned")]
""
`shouldRespondWith`
""
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-5/6"]
}
context "with range headers" $ do
context "of acceptable range" $ do
+5 -4
View File
@@ -15,7 +15,7 @@ import SpecHelper
-- creates Item to work with for PATCH and DELETE
postItem =
request methodPost "/items"
[("Prefer", "resolution=ignore-duplicates")]
[("Prefer", "tx=commit"), ("Prefer", "resolution=ignore-duplicates")]
[json|{"id":0}|]
`shouldRespondWith`
""
@@ -23,7 +23,9 @@ postItem =
-- removes Items left over from POST, PUT, and PATCH
deleteItems =
delete "/items?id=lte.0"
request methodDelete "/items?id=lte.0"
[("Prefer", "tx=commit")]
""
`shouldRespondWith`
""
{ matchStatus = 204 }
@@ -175,9 +177,8 @@ shouldNotPersistMutations reqHeaders respHeaders = do
allowed :: SpecWith ((), Application)
allowed = describe "tx-allow-override = true" $ do
describe "without Prefer tx" $ do
-- TODO: Change this to default to rollback for whole test-suite
preferDefault `shouldRespondToReads` withoutPreferenceApplied
preferDefault `shouldPersistMutations` withoutPreferenceApplied
preferDefault `shouldNotPersistMutations` withoutPreferenceApplied
describe "Prefer tx=commit" $ do
preferCommit `shouldRespondToReads` withPreferenceCommitApplied
+57 -40
View File
@@ -11,62 +11,79 @@ import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import Text.Heredoc
import Protolude hiding (get)
import Protolude hiding (get, put)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
describe "GUC headers on all methods via pre-request" $ do
it "succeeds setting the headers on POST" $
request methodPost "/items" [] [json|[{"id": 11111}]|]
post "/items"
[json|[{"id": 11111}]|]
`shouldRespondWith` ""
{ matchStatus = 201
, matchHeaders = ["X-Custom-Header" <:> "mykey=myval"]
}
{ matchStatus = 201
, matchHeaders = ["X-Custom-Header" <:> "mykey=myval"]
}
it "succeeds setting the headers on GET and HEAD" $ do
request methodGet "/items?id=eq.11111" [("User-Agent", "MSIE 6.0")] mempty
`shouldRespondWith` [json|[{"id": 11111}]|]
{matchHeaders = [
matchContentTypeJson,
"Cache-Control" <:> "no-cache, no-store, must-revalidate"]}
request methodGet "/items?id=eq.1"
[("User-Agent", "MSIE 6.0")]
""
`shouldRespondWith`
[json|[{"id": 1}]|]
{ matchHeaders = ["Cache-Control" <:> "no-cache, no-store, must-revalidate"] }
request methodHead "/items?id=eq.11111" [("User-Agent", "MSIE 7.0")] mempty
`shouldRespondWith` ""
{matchHeaders = ["Cache-Control" <:> "no-cache, no-store, must-revalidate"]}
request methodHead "/items?id=eq.1"
[("User-Agent", "MSIE 7.0")]
""
`shouldRespondWith`
""
{ matchHeaders = ["Cache-Control" <:> "no-cache, no-store, must-revalidate"] }
request methodHead "/projects" [("Accept", "text/csv")] mempty
`shouldRespondWith` ""
{matchHeaders = ["Content-Disposition" <:> "attachment; filename=projects.csv"]}
request methodHead "/projects"
[("Accept", "text/csv")]
""
`shouldRespondWith`
""
{ matchHeaders = ["Content-Disposition" <:> "attachment; filename=projects.csv"] }
it "succeeds setting the headers on PATCH" $
request methodPatch "/items?id=eq.11111" [] [json|[{"id": 11111}]|]
`shouldRespondWith` ""
{ matchStatus = 204
, matchHeaders = ["X-Custom-Header" <:> "mykey=myval"]
}
patch "/items?id=eq.1"
[json|[{"id": 11111}]|]
`shouldRespondWith` ""
{ matchStatus = 204
, matchHeaders = ["X-Custom-Header" <:> "mykey=myval"]
}
it "succeeds setting the headers on PUT" $
request methodPut "/items?id=eq.11111" [] [json|[{"id": 11111}]|]
put "/items?id=eq.1"
[json|[{"id": 1}]|]
`shouldRespondWith` ""
{ matchStatus = 204
, matchHeaders = ["X-Custom-Header" <:> "mykey=myval"]
}
{ matchStatus = 204
, matchHeaders = ["X-Custom-Header" <:> "mykey=myval"]
}
it "succeeds setting the headers on DELETE" $
request methodDelete "/items?id=eq.11111" [] mempty
`shouldRespondWith` ""
{ matchStatus = 204
, matchHeaders = ["X-Custom-Header" <:> "mykey=myval"]
}
delete "/items?id=eq.1"
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = ["X-Custom-Header" <:> "mykey=myval"]
}
it "can override the Content-Type header" $ do
request methodHead "/clients?id=eq.1" [] mempty
`shouldRespondWith` ""
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/custom+json"]
}
request methodHead "/rpc/getallprojects" [] mempty
`shouldRespondWith` ""
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/custom+json"]
}
request methodHead "/clients?id=eq.1"
[]
""
`shouldRespondWith`
""
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/custom+json"]
}
request methodHead "/rpc/getallprojects"
[]
""
`shouldRespondWith`
""
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/custom+json"]
}
+29 -13
View File
@@ -457,12 +457,24 @@ spec actualPgVersion =
`shouldRespondWith` 405
it "executes the proc exactly once per request" $ do
post "/rpc/callcounter" [json| {} |] `shouldRespondWith`
[json|1|]
{ matchHeaders = [matchContentTypeJson] }
post "/rpc/callcounter" [json| {} |] `shouldRespondWith`
[json|2|]
{ matchHeaders = [matchContentTypeJson] }
-- callcounter is persistent even with rollback, because it uses a sequence
-- reset counter first to make test repeatable
request methodPost "/rpc/reset_sequence"
[("Prefer", "tx=commit")]
[json|{"name": "callcounter_count", "value": 1}|]
`shouldRespondWith`
[json|""|]
-- now the test
post "/rpc/callcounter"
[json|{}|]
`shouldRespondWith`
[json|1|]
post "/rpc/callcounter"
[json|{}|]
`shouldRespondWith`
[json|2|]
context "a proc that receives no parameters" $ do
it "interprets empty string as empty json object on a post request" $
@@ -891,11 +903,13 @@ spec actualPgVersion =
"Set-Cookie" <:> "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly"]}
it "can override the Location header on a trigger" $
request methodPost "/stuff" [] [json|[{"id": 1, "name": "stuff 1"}]|]
`shouldRespondWith` ""
{ matchStatus = 201
, matchHeaders = ["Location" <:> "/stuff?id=eq.1&overriden=true"]
}
post "/stuff"
[json|[{"id": 2, "name": "stuff 2"}]|]
`shouldRespondWith`
""
{ matchStatus = 201
, matchHeaders = ["Location" <:> "/stuff?id=eq.2&overriden=true"]
}
-- On https://github.com/PostgREST/postgrest/issues/1427#issuecomment-595907535
-- it was reported that blank headers ` : ` where added and that cause proxies to fail the requests.
@@ -935,8 +949,10 @@ spec actualPgVersion =
}
it "can override the status through trigger" $
request methodPatch "/stuff?id=eq.1" [] [json|[{"name": "updated stuff 1"}]|]
`shouldRespondWith` 205
patch "/stuff?id=eq.1"
[json|[{"name": "updated stuff 1"}]|]
`shouldRespondWith`
205
it "fails when setting invalid status guc" $
get "/rpc/send_bad_status"
+119 -89
View File
@@ -15,8 +15,7 @@ import SpecHelper
spec :: SpecWith ((), Application)
spec =
describe "Requesting singular json object" $ do
let pgrstObj = "application/vnd.pgrst.object+json"
singular = ("Accept", pgrstObj)
let singular = ("Accept", "application/vnd.pgrst.object+json")
context "with GET request" $ do
it "fails for zero rows" $
@@ -49,48 +48,52 @@ spec =
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" } |]
request methodPatch "/addresses?id=eq.1"
[("Prefer", "return=representation"), singular]
[json| { address: "B Street" } |]
`shouldRespondWith`
[json|{"id":97,"address":"B Street"}|]
[json|{"id":1,"address":"B Street"}|]
{ matchHeaders = [matchContentTypeSingular] }
it "works for one row with return=minimal" $
request methodPatch
"/addresses?id=eq.97"
[("Prefer", "return=minimal"), singular]
[json| { address: "C Street" } |]
request methodPatch "/addresses?id=eq.1"
[("Prefer", "return=minimal"), singular]
[json| { address: "C Street" } |]
`shouldRespondWith`
""
{ matchStatus = 204 }
it "raises an error for multiple rows" $ do
_ <- post "/addresses" [json| { id: 98, address: "xxx" } |]
_ <- post "/addresses" [json| { id: 99, address: "yyy" } |]
p <- request methodPatch "/addresses?id=gt.0"
[singular]
[json| { address: "zzz" } |]
liftIO $ do
simpleStatus p `shouldBe` notAcceptable406
isErrorFormat (simpleBody p) `shouldBe` True
request methodPatch "/addresses"
[("Prefer", "tx=commit"), singular]
[json| { address: "zzz" } |]
`shouldRespondWith`
[json|{"details":"Results contain 4 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|]
{ matchStatus = 406
, matchHeaders = [ matchContentTypeSingular
, "Preference-Applied" <:> "tx=commit" ]
}
-- the rows should not be updated, either
get "/addresses?id=eq.98" `shouldRespondWith` [json|[{"id":98,"address":"xxx"}]|]
get "/addresses?id=eq.1"
`shouldRespondWith`
[json|[{"id":1,"address":"address 1"}]|]
it "raises an error for multiple rows with return=rep" $ do
_ <- post "/addresses" [json| { id: 100, address: "xxx" } |]
_ <- post "/addresses" [json| { id: 101, address: "yyy" } |]
p <- request methodPatch "/addresses?id=gt.0"
[("Prefer", "return=representation"), singular]
[json| { address: "zzz" } |]
liftIO $ do
simpleStatus p `shouldBe` notAcceptable406
isErrorFormat (simpleBody p) `shouldBe` True
request methodPatch "/addresses"
[("Prefer", "tx=commit"), ("Prefer", "return=representation"), singular]
[json| { address: "zzz" } |]
`shouldRespondWith`
[json|{"details":"Results contain 4 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|]
{ matchStatus = 406
, matchHeaders = [ matchContentTypeSingular
, "Preference-Applied" <:> "tx=commit" ]
}
-- the rows should not be updated, either
get "/addresses?id=eq.100" `shouldRespondWith` [json|[{"id":100,"address":"xxx"}]|]
get "/addresses?id=eq.1"
`shouldRespondWith`
[json|[{"id":1,"address":"address 1"}]|]
it "raises an error for zero rows" $
request methodPatch "/items?id=gt.0&id=lt.0"
@@ -123,51 +126,60 @@ spec =
it "works for one row with return=minimal" $ do
request methodPost "/addresses"
[("Prefer", "return=minimal"), singular]
[json| [ { id: 103, address: "xxx" } ] |]
[("Prefer", "return=minimal"), singular]
[json| [ { id: 103, address: "xxx" } ] |]
`shouldRespondWith` ""
{ matchStatus = 201
, matchHeaders = ["Content-Range" <:> "*/*"]
}
-- and the element should exist
get "/addresses?id=eq.103"
`shouldRespondWith` [json|[{"id":103,"address":"xxx"}]|]
{ matchStatus = 200
, matchHeaders = []
}
{ matchStatus = 201
, matchHeaders = ["Content-Range" <:> "*/*"]
}
it "raises an error when attempting to create multiple entities" $ do
p <- request methodPost
"/addresses"
[singular]
[json| [ { id: 200, address: "xxx" }, { id: 201, address: "yyy" } ] |]
liftIO $ simpleStatus p `shouldBe` notAcceptable406
request methodPost "/addresses"
[("Prefer", "tx=commit"), singular]
[json| [ { id: 200, address: "xxx" }, { id: 201, address: "yyy" } ] |]
`shouldRespondWith`
[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 = [ matchContentTypeSingular
, "Preference-Applied" <:> "tx=commit" ]
}
-- the rows should not exist, either
get "/addresses?id=eq.200" `shouldRespondWith` "[]"
get "/addresses?id=eq.200"
`shouldRespondWith`
"[]"
it "raises an error when attempting to create multiple entities with return=rep" $ do
p <- request methodPost
"/addresses"
[("Prefer", "return=representation"), singular]
[json| [ { id: 202, address: "xxx" }, { id: 203, address: "yyy" } ] |]
liftIO $ simpleStatus p `shouldBe` notAcceptable406
request methodPost "/addresses"
[("Prefer", "tx=commit"), ("Prefer", "return=representation"), singular]
[json| [ { id: 202, address: "xxx" }, { id: 203, address: "yyy" } ] |]
`shouldRespondWith`
[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 = [ matchContentTypeSingular
, "Preference-Applied" <:> "tx=commit" ]
}
-- the rows should not exist, either
get "/addresses?id=eq.202" `shouldRespondWith` "[]"
get "/addresses?id=eq.202"
`shouldRespondWith`
"[]"
it "raises an error regardless of return=minimal" $ do
request methodPost "/addresses"
[("Prefer", "return=minimal"), singular]
[json| [ { id: 204, address: "xxx" }, { id: 205, address: "yyy" } ] |]
[("Prefer", "tx=commit"), ("Prefer", "return=minimal"), singular]
[json| [ { id: 204, address: "xxx" }, { id: 205, address: "yyy" } ] |]
`shouldRespondWith`
[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 = [matchContentTypeSingular]
}
[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 = [ matchContentTypeSingular
, "Preference-Applied" <:> "tx=commit" ]
}
-- the rows should not exist, either
get "/addresses?id=eq.204" `shouldRespondWith` "[]"
get "/addresses?id=eq.204"
`shouldRespondWith`
"[]"
it "raises an error when creating zero entities" $
request methodPost "/addresses"
@@ -203,28 +215,40 @@ spec =
liftIO $ simpleBody p `shouldBe` ""
it "raises an error when attempting to delete multiple entities" $ do
let firstItems = "/items?id=gt.0&id=lt.6"
request methodDelete firstItems
[singular] ""
`shouldRespondWith` 406
request methodDelete "/items?id=gt.0&id=lt.6"
[("Prefer", "tx=commit"), singular]
""
`shouldRespondWith`
[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 = [ matchContentTypeSingular
, "Preference-Applied" <:> "tx=commit" ]
}
get firstItems
`shouldRespondWith` [json| [{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}] |]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-4/*"]
}
-- the rows should still exist
get "/items?id=gt.0&id=lt.6&order=id"
`shouldRespondWith`
[json| [{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}] |]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-4/*"]
}
it "raises an error when attempting to delete multiple entities with return=rep" $ do
let firstItems = "/items?id=gt.5&id=lt.11"
request methodDelete firstItems
[("Prefer", "return=representation"), singular] ""
`shouldRespondWith` 406
request methodDelete "/items?id=gt.5&id=lt.11"
[("Prefer", "tx=commit"), ("Prefer", "return=representation"), singular] ""
`shouldRespondWith`
[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 = [ matchContentTypeSingular
, "Preference-Applied" <:> "tx=commit" ]
}
get firstItems
-- the rows should still exist
get "/items?id=gt.5&id=lt.11"
`shouldRespondWith` [json| [{"id":6},{"id":7},{"id":8},{"id":9},{"id":10}] |]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-4/*"]
}
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "0-4/*"]
}
it "raises an error when deleting zero entities" $
request methodDelete "/items?id=lt.0"
@@ -277,18 +301,24 @@ spec =
, matchHeaders = [matchContentTypeSingular]
}
it "executes the proc exactly once per request" $ do
request methodPost "/rpc/getproject?select=id,name" [] [json| {"id": 1} |]
`shouldRespondWith` [json|[{"id":1,"name":"Windows 7"}]|]
request methodPost "/rpc/setprojects" [singular]
[json| {"id_l": 1, "id_h": 2, "name": "changed"} |]
it "fails for multiple rows with rolled back changes" $ do
post "/rpc/getproject?select=id,name"
[json| {"id": 1} |]
`shouldRespondWith`
[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 = [matchContentTypeSingular]
}
[json|[{"id":1,"name":"Windows 7"}]|]
-- should not actually have executed the function
request methodPost "/rpc/getproject?select=id,name" [] [json| {"id": 1} |]
`shouldRespondWith` [json|[{"id":1,"name":"Windows 7"}]|]
request methodPost "/rpc/setprojects"
[("Prefer", "tx=commit"), singular]
[json| {"id_l": 1, "id_h": 2, "name": "changed"} |]
`shouldRespondWith`
[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 = [ matchContentTypeSingular
, "Preference-Applied" <:> "tx=commit" ]
}
-- should rollback function
post "/rpc/getproject?select=id,name"
[json| {"id": 1} |]
`shouldRespondWith`
[json|[{"id":1,"name":"Windows 7"}]|]
+15 -4
View File
@@ -2,6 +2,7 @@ module Feature.UnicodeSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
@@ -16,9 +17,19 @@ spec =
get "/%D9%85%D9%88%D8%A7%D8%B1%D8%AF"
`shouldRespondWith` "[]"
void $ post "/%D9%85%D9%88%D8%A7%D8%B1%D8%AF"
[json| { "هویت": 1 } |]
request methodPost "/%D9%85%D9%88%D8%A7%D8%B1%D8%AF"
[("Prefer", "tx=commit"), ("Prefer", "return=representation")]
[json| { "هویت": 1 } |]
`shouldRespondWith`
[json| [{ "هویت": 1 }] |]
{ matchStatus = 201 }
get "/%D9%85%D9%88%D8%A7%D8%B1%D8%AF"
`shouldRespondWith` [json| [{ "هویت": 1 }] |]
{ matchHeaders = [matchContentTypeJson] }
`shouldRespondWith`
[json| [{ "هویت": 1 }] |]
request methodDelete "/%D9%85%D9%88%D8%A7%D8%B1%D8%AF"
[("Prefer", "tx=commit")]
""
`shouldRespondWith`
204
+62 -42
View File
@@ -51,22 +51,31 @@ spec = do
context "in a nonempty table" $ do
it "can update a single item" $ do
g <- get "/items?id=eq.42"
liftIO $ simpleHeaders g
`shouldSatisfy` matchHeader "Content-Range" "\\*/\\*"
p <- request methodPatch "/items?id=eq.2" [] [json| { "id":42 } |]
pure p `shouldRespondWith` ""
{ matchStatus = 204,
matchHeaders = ["Content-Range" <:> "0-0/*"]
}
liftIO $ lookup hContentType (simpleHeaders p) `shouldBe` Nothing
get "/items?id=eq.42"
`shouldRespondWith`
[json|[]|]
request methodPatch "/items?id=eq.2"
[("Prefer", "tx=commit")]
[json| { "id":42 } |]
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = ["Content-Range" <:> "0-0/*"
, "Preference-Applied" <:> "tx=commit" ]
}
-- check it really got updated
g' <- get "/items?id=eq.42"
liftIO $ simpleHeaders g'
`shouldSatisfy` matchHeader "Content-Range" "0-0/\\*"
get "/items?id=eq.42"
`shouldRespondWith`
[json|[ { "id": 42 } ]|]
-- put value back for other tests
void $ request methodPatch "/items?id=eq.42" [] [json| { "id":2 } |]
request methodPatch "/items?id=eq.42"
[("Prefer", "tx=commit")]
[json| { "id":2 } |]
`shouldRespondWith`
204
it "returns empty array when no rows updated and return=rep" $
request methodPatch "/items?id=eq.999999"
@@ -91,23 +100,38 @@ spec = do
}
it "can update multiple items" $ do
replicateM_ 10 $ post "/auto_incrementing_pk"
[json| { non_nullable_string: "a" } |]
replicateM_ 10 $ post "/auto_incrementing_pk"
[json| { non_nullable_string: "b" } |]
_ <- request methodPatch
"/auto_incrementing_pk?non_nullable_string=eq.a" []
[json| { non_nullable_string: "c" } |]
g <- get "/auto_incrementing_pk?non_nullable_string=eq.c"
liftIO $ simpleHeaders g
`shouldSatisfy` matchHeader "Content-Range" "0-9/\\*"
get "/no_pk?select=a&b=eq.1"
`shouldRespondWith`
[json|[]|]
request methodPatch "/no_pk?b=eq.0"
[("Prefer", "tx=commit")]
[json| { b: "1" } |]
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = ["Content-Range" <:> "0-1/*"
, "Preference-Applied" <:> "tx=commit" ]
}
-- check it really got updated
get "/no_pk?select=a&b=eq.1"
`shouldRespondWith`
[json|[ { a: "1" }, { a: "2" } ]|]
-- put value back for other tests
request methodPatch "/no_pk?b=eq.1"
[("Prefer", "tx=commit")]
[json| { b: "0" } |]
`shouldRespondWith`
204
it "can set a column to NULL" $ do
_ <- post "/no_pk" [json| { a: "keepme", b: "nullme" } |]
_ <- request methodPatch "/no_pk?b=eq.nullme" [] [json| { b: null } |]
get "/no_pk?a=eq.keepme" `shouldRespondWith`
[json| [{ a: "keepme", b: null }] |]
{ matchHeaders = [matchContentTypeJson] }
request methodPatch "/no_pk?a=eq.1"
[("Prefer", "return=representation")]
[json| { b: null } |]
`shouldRespondWith`
[json| [{ a: "1", b: null }] |]
context "filtering by a computed column" $ do
it "is successful" $
@@ -250,23 +274,19 @@ spec = do
context "with unicode values" $
it "succeeds and returns values intact" $ do
void $ request methodPost "/no_pk" []
[json| { "a":"patchme", "b":"patchme" } |]
let payload = [json| { "a":"圍棋", "b":"" } |]
p <- request methodPatch "/no_pk?a=eq.patchme&b=eq.patchme"
[("Prefer", "return=representation")] payload
liftIO $ do
simpleBody p `shouldBe` "["<>payload<>"]"
simpleStatus p `shouldBe` ok200
request methodPatch "/no_pk?a=eq.1"
[("Prefer", "return=representation")]
[json| { "a":"圍棋", "b":"" } |]
`shouldRespondWith`
[json|[ { "a":"圍棋", "b":"" } ]|]
context "PATCH with ?columns parameter" $ do
it "ignores json keys not included in ?columns" $ do
post "/articles?columns=id,body" [json| {"id": 200} |]
request methodPatch "/articles?id=eq.200&columns=body" [("Prefer", "return=representation")]
[json| {"body": "Some real content", "smth": "here", "other": "stuff", "fake_id": 13} |] `shouldRespondWith`
[json|[{"id": 200, "body": "Some real content", "owner": "postgrest_test_anonymous"}]|]
{ matchStatus = 200
, matchHeaders = [] }
request methodPatch "/articles?id=eq.1&columns=body"
[("Prefer", "return=representation")]
[json| {"body": "Some real content", "smth": "here", "other": "stuff", "fake_id": 13} |]
`shouldRespondWith`
[json|[{"id": 1, "body": "Some real content", "owner": "postgrest_test_anonymous"}]|]
it "ignores json keys and gives 404 if no record updated" $
request methodPatch "/articles?id=eq.2001&columns=body" [("Prefer", "return=representation")]
+131 -78
View File
@@ -100,30 +100,38 @@ spec =
}
it "INSERTs and ignores rows on single unique key conflict" $
request methodPost "/single_unique?on_conflict=unique_key" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
[json| [
{ "unique_key": 1, "value": "B" },
{ "unique_key": 2, "value": "C" },
{ "unique_key": 3, "value": "D" }
]|] `shouldRespondWith` [json| [
{ "unique_key": 3, "value": "D" }
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates", matchContentTypeJson]
}
request methodPost "/single_unique?on_conflict=unique_key"
[("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
[json| [
{ "unique_key": 1, "value": "B" },
{ "unique_key": 2, "value": "C" },
{ "unique_key": 3, "value": "D" }
]|]
`shouldRespondWith`
[json| [
{ "unique_key": 2, "value": "C" },
{ "unique_key": 3, "value": "D" }
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates"]
}
it "INSERTs and UPDATEs rows on compound unique keys conflict" $
request methodPost "/compound_unique?on_conflict=key1,key2" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
[json| [
{ "key1": 1, "key2": 1, "value": "B" },
{ "key1": 1, "key2": 2, "value": "C" },
{ "key1": 1, "key2": 3, "value": "D" }
]|] `shouldRespondWith` [json| [
{ "key1": 1, "key2": 3, "value": "D" }
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates", matchContentTypeJson]
}
request methodPost "/compound_unique?on_conflict=key1,key2"
[("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
[json| [
{ "key1": 1, "key2": 1, "value": "B" },
{ "key1": 1, "key2": 2, "value": "C" },
{ "key1": 1, "key2": 3, "value": "D" }
]|]
`shouldRespondWith`
[json| [
{ "key1": 1, "key2": 2, "value": "C" },
{ "key1": 1, "key2": 3, "value": "D" }
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates"]
}
it "succeeds if the table has only PK cols and no other cols" $ do
request methodPost "/only_pk" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
@@ -233,58 +241,93 @@ 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" [json| [ { "name": "Go", "rank": 19 } ]|] `shouldRespondWith` 204
get "/tiobe_pls?name=eq.Go" `shouldRespondWith` [json| [ { "name": "Go", "rank": 19 } ]|] { matchHeaders = [matchContentTypeJson] }
-- assert that the next request will indeed be an insert
get "/tiobe_pls?name=eq.Go"
`shouldRespondWith`
[json|[]|]
request methodPut "/tiobe_pls?name=eq.Go"
[("Prefer", "return=representation")]
[json| [ { "name": "Go", "rank": 19 } ]|]
`shouldRespondWith`
[json| [ { "name": "Go", "rank": 19 } ]|]
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"
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|]
`shouldRespondWith` 204
-- assert that the next request will indeed be an insert
get "/employees?first_name=eq.Susan&last_name=eq.Heidt"
`shouldRespondWith`
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "$48,000.00", "company": "GEX", "occupation": "Railroad engineer" } ]|]
{ matchHeaders = [matchContentTypeJson] }
[json|[]|]
request methodPut "/employees?first_name=eq.Susan&last_name=eq.Heidt"
[("Prefer", "return=representation")]
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|]
`shouldRespondWith`
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "$48,000.00", "company": "GEX", "occupation": "Railroad engineer" } ]|]
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" [json|[ { "id": 10 } ]|] `shouldRespondWith` 204
get "/only_pk?id=eq.10" `shouldRespondWith` [json|[ { "id": 10 } ]|] { matchHeaders = [matchContentTypeJson] }
-- assert that the next request will indeed be an insert
get "/only_pk?id=eq.10"
`shouldRespondWith`
[json|[]|]
request methodPut "/only_pk?id=eq.10"
[("Prefer", "return=representation")]
[json|[ { "id": 10 } ]|]
`shouldRespondWith`
[json|[ { "id": 10 } ]|]
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" [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")]
[json| [ { "name": "Go", "rank": 19 }, { "name": "Swift", "rank": 12 } ] |]
-- assert that the next request will indeed be an update
get "/tiobe_pls?name=eq.Java"
`shouldRespondWith`
[json|{ "name": "Go", "rank": 19 }|]
{ matchStatus = 200 , matchHeaders = [matchContentTypeSingular] }
[json|[ { "name": "Java", "rank": 1 } ]|]
request methodPut "/tiobe_pls?name=eq.Java"
[("Prefer", "return=representation")]
[json| [ { "name": "Java", "rank": 13 } ]|]
`shouldRespondWith`
[json| [ { "name": "Java", "rank": 13 } ]|]
-- TODO: move this to SingularSpec?
it "succeeds if the payload has more than one row, but it only puts the first element" $ do
-- assert that the next request will indeed be an update
get "/tiobe_pls?name=eq.Java"
`shouldRespondWith`
[json|[ { "name": "Java", "rank": 1 } ]|]
request methodPut "/tiobe_pls?name=eq.Java"
[("Prefer", "return=representation"), ("Accept", "application/vnd.pgrst.object+json")]
[json| [ { "name": "Java", "rank": 19 }, { "name": "Swift", "rank": 12 } ] |]
`shouldRespondWith`
[json|{ "name": "Java", "rank": 19 }|]
{ matchHeaders = [matchContentTypeSingular] }
it "succeeds on table with composite pk" $ do
get "/employees?first_name=eq.Susan&last_name=eq.Heidt"
-- assert that the next request will indeed be an update
get "/employees?first_name=eq.Frances M.&last_name=eq.Roe"
`shouldRespondWith`
[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"
[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"
[json| [ { "first_name": "Frances M.", "last_name": "Roe", "salary": "$24,000.00", "company": "One-Up Realty", "occupation": "Author" } ]|]
request methodPut "/employees?first_name=eq.Frances M.&last_name=eq.Roe"
[("Prefer", "return=representation")]
[json| [ { "first_name": "Frances M.", "last_name": "Roe", "salary": "60000", "company": "Gamma Gas", "occupation": "Railroad engineer" } ]|]
`shouldRespondWith`
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "$60,000.00", "company": "Gamma Gas", "occupation": "Railroad engineer" } ]|]
{ matchHeaders = [matchContentTypeJson] }
[json| [ { "first_name": "Frances M.", "last_name": "Roe", "salary": "$60,000.00", "company": "Gamma Gas", "occupation": "Railroad engineer" } ]|]
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" [json|[ { "id": 10 } ]|] `shouldRespondWith` 204
get "/only_pk?id=eq.10" `shouldRespondWith` [json|[ { "id": 10 } ]|] { matchHeaders = [matchContentTypeJson] }
-- assert that the next request will indeed be an update
get "/only_pk?id=eq.1"
`shouldRespondWith`
[json|[ { "id": 1 } ]|]
request methodPut "/only_pk?id=eq.1"
[("Prefer", "return=representation")]
[json|[ { "id": 1 } ]|]
`shouldRespondWith`
[json|[ { "id": 1 } ]|]
-- TODO: move this to SingularSpec?
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")]
@@ -292,28 +335,38 @@ spec =
`shouldRespondWith` [json|{ "name": "Ruby", "rank": 11 }|] { matchHeaders = [matchContentTypeSingular] }
context "with a camel case pk column" $ do
it "works with POST and merge-duplicates/ignore-duplicates headers" $ do
request methodPost "/UnitTest" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
[json| [
{ "idUnitTest": 1, "nameUnitTest": "name of unittest 1" },
{ "idUnitTest": 2, "nameUnitTest": "name of unittest 2" }
]|] `shouldRespondWith` [json|[
{ "idUnitTest": 1, "nameUnitTest": "name of unittest 1" },
{ "idUnitTest": 2, "nameUnitTest": "name of unittest 2" }
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates", matchContentTypeJson]
}
request methodPost "/UnitTest" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
[json| [
{ "idUnitTest": 1, "nameUnitTest": "name of unittest 1" },
{ "idUnitTest": 2, "nameUnitTest": "name of unittest 2" }
]|] `shouldRespondWith` [json|[]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates", matchContentTypeJson]
}
it "works with POST and merge-duplicates" $ do
request methodPost "/UnitTest"
[("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
[json|[
{ "idUnitTest": 1, "nameUnitTest": "name of unittest 1" },
{ "idUnitTest": 2, "nameUnitTest": "name of unittest 2" }
]|]
`shouldRespondWith`
[json|[
{ "idUnitTest": 1, "nameUnitTest": "name of unittest 1" },
{ "idUnitTest": 2, "nameUnitTest": "name of unittest 2" }
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates"]
}
it "works with POST and ignore-duplicates headers" $ do
request methodPost "/UnitTest"
[("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
[json|[
{ "idUnitTest": 1, "nameUnitTest": "name of unittest 1" },
{ "idUnitTest": 2, "nameUnitTest": "name of unittest 2" }
]|]
`shouldRespondWith`
[json|[
{ "idUnitTest": 2, "nameUnitTest": "name of unittest 2" }
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates"]
}
it "works with PUT" $ do
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] }
[json| [ { "idUnitTest": 1, "nameUnitTest": "unit test 1" } ]|]
+9 -16
View File
@@ -96,39 +96,32 @@ main = do
nonexistentSchemaApp = appDbs testNonexistentSchemaCfg
multipleSchemaApp = appDbs testMultipleSchemaCfg
let reset, analyze :: IO ()
reset = resetDb testDbConn
let analyze :: IO ()
analyze = do
analyzeTable testDbConn "items"
analyzeTable testDbConn "child_entities"
specs = uncurry describe <$> [
("Feature.AuthSpec" , Feature.AuthSpec.spec actualPgVersion)
, ("Feature.RawOutputTypesSpec" , Feature.RawOutputTypesSpec.spec)
("Feature.AndOrParamsSpec" , Feature.AndOrParamsSpec.spec actualPgVersion)
, ("Feature.AuthSpec" , Feature.AuthSpec.spec actualPgVersion)
, ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec)
, ("Feature.CorsSpec" , Feature.CorsSpec.spec)
, ("Feature.DeleteSpec" , Feature.DeleteSpec.spec)
, ("Feature.EmbedDisambiguationSpec" , Feature.EmbedDisambiguationSpec.spec)
, ("Feature.InsertSpec" , Feature.InsertSpec.spec actualPgVersion)
, ("Feature.JsonOperatorSpec" , Feature.JsonOperatorSpec.spec actualPgVersion)
, ("Feature.OpenApiSpec" , Feature.OpenApiSpec.spec)
, ("Feature.OptionsSpec" , Feature.OptionsSpec.spec)
, ("Feature.QuerySpec" , Feature.QuerySpec.spec actualPgVersion)
, ("Feature.EmbedDisambiguationSpec" , Feature.EmbedDisambiguationSpec.spec)
, ("Feature.RawOutputTypesSpec" , Feature.RawOutputTypesSpec.spec)
, ("Feature.RollbackAllowedSpec" , Feature.RollbackSpec.allowed)
, ("Feature.RpcSpec" , Feature.RpcSpec.spec actualPgVersion)
, ("Feature.AndOrParamsSpec" , Feature.AndOrParamsSpec.spec actualPgVersion)
, ("Feature.SingularSpec" , Feature.SingularSpec.spec)
, ("Feature.UpdateSpec" , Feature.UpdateSpec.spec)
, ("Feature.UpsertSpec" , Feature.UpsertSpec.spec)
]
mutSpecs = uncurry describe <$> [
("Feature.DeleteSpec" , Feature.DeleteSpec.spec)
, ("Feature.InsertSpec" , Feature.InsertSpec.spec actualPgVersion)
, ("Feature.SingularSpec" , Feature.SingularSpec.spec)
, ("Feature.UpdateSpec" , Feature.UpdateSpec.spec)
]
hspec $ do
-- Only certain Specs need a database reset, this should be used with care as it slows down the whole test suite.
mapM_ (afterAll_ reset . before withApp) mutSpecs
mapM_ (before withApp) specs
-- we analyze to get accurate results from EXPLAIN
+1 -1
View File
@@ -90,7 +90,7 @@ _baseCfg = let secret = Just $ encodeUtf8 "reallyreallyreallyreallyverysafe" in
, configRawMediaTypes = []
, configJWKS = parseSecret <$> secret
, configLogLevel = LogCrit
, configTxRollbackAll = False
, configTxRollbackAll = True
, configTxAllowOverride = True
, configDbPrepared = True
}
+12
View File
@@ -359,6 +359,9 @@ TRUNCATE TABLE child_entities CASCADE;
INSERT INTO child_entities VALUES (1, 'child entity 1', 1);
INSERT INTO child_entities VALUES (2, 'child entity 2', 1);
INSERT INTO child_entities VALUES (3, 'child entity 3', 2);
INSERT INTO child_entities VALUES (4, 'child entity 4', 1);
INSERT INTO child_entities VALUES (5, 'child entity 5', 1);
INSERT INTO child_entities VALUES (6, 'child entity 6', 2);
TRUNCATE TABLE grandchild_entities CASCADE;
INSERT INTO grandchild_entities VALUES (1, 'grandchild entity 1', 1, null, null, null);
@@ -626,8 +629,17 @@ INSERT INTO unit_workdays VALUES(1, '2019-12-02', 1, 1, 2, 3);
TRUNCATE TABLE v1.parents CASCADE;
INSERT INTO v1.parents VALUES(1, 'parent v1-1'), (2, 'parent v1-2');
TRUNCATE TABLE v1.children CASCADE;
INSERT INTO v1.children VALUES(1, 'child v1-1', 1), (2, 'child v1-2', 2);
TRUNCATE TABLE v2.parents CASCADE;
INSERT INTO v2.parents VALUES(3, 'parent v2-3'), (4, 'parent v2-4');
TRUNCATE TABLE v2.children CASCADE;
INSERT INTO v2.children VALUES(1, 'child v2-3', 3);
TRUNCATE TABLE v2.another_table CASCADE;
INSERT INTO v2.another_table VALUES(5, 'value 5'), (6, 'value 6');
TRUNCATE TABLE private.stuff CASCADE;
INSERT INTO private.stuff (id, name) VALUES (1, 'stuff 1');
-2
View File
@@ -144,8 +144,6 @@ GRANT USAGE ON SEQUENCE
, items_id_seq
, callcounter_count
, leak_id_seq
, v1.children_id_seq
, v2.children_id_seq
TO postgrest_test_anonymous;
-- Privileges for non anonymous users
+13 -3
View File
@@ -365,6 +365,16 @@ CREATE FUNCTION callcounter() RETURNS bigint
SELECT nextval('test.callcounter_count');
$_$;
CREATE FUNCTION reset_sequence(name TEXT, value INTEGER) RETURNS void
SECURITY DEFINER
LANGUAGE plpgsql AS $_$
BEGIN
EXECUTE FORMAT($exec$
ALTER SEQUENCE %s RESTART WITH %s
$exec$, name, value);
END
$_$;
--
-- Name: singlejsonparam(json); Type: FUNCTION; Schema: test; Owner: -
--
@@ -1825,8 +1835,8 @@ create table v1.parents (
);
create table v1.children (
id serial primary key
, name text
id int primary key
, name text
, parent_id int
, constraint parent foreign key(parent_id)
references v1.parents(id)
@@ -1843,7 +1853,7 @@ create table v2.parents (
);
create table v2.children (
id serial primary key
id int primary key
, name text
, parent_id int
, constraint parent foreign key(parent_id)