feat: add Content-Length response header

This commit is contained in:
Laurence Isla
2025-04-24 12:49:18 -05:00
committed by Steve Chavez
parent 98fcbedca5
commit 57ef9988a5
23 changed files with 298 additions and 114 deletions
+9 -9
View File
@@ -964,45 +964,45 @@ def test_log_level(level, defaultenv):
assert len(output) == 0
elif level == "error":
assert re.match(
r'- - - \[.+\] "GET / HTTP/1.1" 500 - "" "python-requests/.+"',
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
output[0],
)
assert len(output) == 1
elif level == "warn":
assert re.match(
r'- - - \[.+\] "GET / HTTP/1.1" 500 - "" "python-requests/.+"',
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
output[0],
)
assert re.match(
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 - "" "python-requests/.+"',
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
output[1],
)
assert len(output) == 2
elif level == "info":
assert re.match(
r'- - - \[.+\] "GET / HTTP/1.1" 500 - "" "python-requests/.+"',
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
output[0],
)
assert re.match(
r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 - "" "python-requests/.+"',
r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+"',
output[1],
)
assert re.match(
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 - "" "python-requests/.+"',
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
output[2],
)
assert len(output) == 3
elif level == "debug":
assert re.match(
r'- - - \[.+\] "GET / HTTP/1.1" 500 - "" "python-requests/.+"',
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
output[0],
)
assert re.match(
r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 - "" "python-requests/.+"',
r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+"',
output[1],
)
assert re.match(
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 - "" "python-requests/.+"',
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
output[2],
)
+5 -3
View File
@@ -22,7 +22,8 @@ spec = describe "authorization" $ do
"code":"42501",
"message":"permission denied for table authors_only"} |]
{ matchStatus = 401
, matchHeaders = ["WWW-Authenticate" <:> "Bearer"]
, matchHeaders = [ "WWW-Authenticate" <:> "Bearer"
, "Content-Length" <:> "96" ]
}
it "denies access to tables that postgrest_test_author does not own" $
@@ -35,7 +36,7 @@ spec = describe "authorization" $ do
"code":"42501",
"message":"permission denied for table private_table"} |]
{ matchStatus = 403
, matchHeaders = []
, matchHeaders = ["Content-Length" <:> "97"]
}
it "denies execution on functions that anonymous does not own" $
@@ -92,7 +93,8 @@ spec = describe "authorization" $ do
{ matchStatus = 401
, matchHeaders = [
"WWW-Authenticate" <:>
"Bearer error=\"invalid_token\", error_description=\"Empty JWT is sent in Authorization header\""
"Bearer error=\"invalid_token\", error_description=\"Empty JWT is sent in Authorization header\"",
"Content-Length" <:> "100"
]
}
@@ -17,4 +17,5 @@ spec =
[("Accept","application/openapi+json")] ""
`shouldRespondWith`
[json| {"code":"PGRST126","details":null,"hint":null,"message":"Root endpoint metadata is disabled"} |]
{ matchStatus = 404 }
{ matchStatus = 404
, matchHeaders = ["Content-Length" <:> "93"]}
@@ -25,7 +25,8 @@ spec = describe "OpenAPI Ignore Privileges" $ do
`shouldRespondWith`
""
{ matchStatus = 200
, matchHeaders = [ "Content-Type" <:> "application/openapi+json; charset=utf-8" ]
, matchHeaders = [ "Content-Type" <:> "application/openapi+json; charset=utf-8"
, matchHeaderAbsent hContentLength]
}
describe "table" $ do
+8 -4
View File
@@ -24,7 +24,8 @@ spec = describe "OpenAPI" $ do
`shouldRespondWith`
""
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/openapi+json; charset=utf-8"]
, matchHeaders = [ "Content-Type" <:> "application/openapi+json; charset=utf-8"
, matchHeaderAbsent hContentLength ]
}
it "should respond to openapi request on none root path with 406" $
@@ -38,11 +39,14 @@ spec = describe "OpenAPI" $ do
`shouldRespondWith` 406
it "includes postgrest.org current version api docs" $ do
r <- simpleBody <$> get "/"
r <- get "/"
let docsUrl = r ^? key "externalDocs" . key "url"
let headers = simpleHeaders r
docsUrl = simpleBody r ^? key "externalDocs" . key "url"
liftIO $ docsUrl `shouldBe` Just (String ("https://postgrest.org/en/" <> docsVersion <> "/references/api.html"))
liftIO $ do
headers `shouldSatisfy` notZeroContentLength
docsUrl `shouldBe` Just (String ("https://postgrest.org/en/" <> docsVersion <> "/references/api.html"))
describe "schema" $ do
+3 -3
View File
@@ -15,9 +15,9 @@ spec = describe "Allow header" $ do
context "a table" $ do
it "includes read/write methods for writeable table" $ do
r <- request methodOptions "/items" [] ""
liftIO $
simpleHeaders r `shouldSatisfy`
matchHeader "Allow" "OPTIONS,GET,HEAD,POST,PUT,PATCH,DELETE"
liftIO $ do
simpleHeaders r `shouldSatisfy` matchHeader "Allow" "OPTIONS,GET,HEAD,POST,PUT,PATCH,DELETE"
simpleHeaders r `shouldSatisfy` matchHeader "Content-Length" "0"
it "fails with 404 for an unknown table" $
request methodOptions "/unknown" [] "" `shouldRespondWith` 404
+29 -12
View File
@@ -20,6 +20,7 @@ spec = describe "custom media types" $ do
liftIO $ do
simpleBody r `shouldBe` readFixtureFile "lines.twkb"
simpleHeaders r `shouldContain` [("Content-Type", "application/vnd.twkb")]
simpleHeaders r `shouldContain` [("Content-Length", "30")]
it "can query by id if there's an aggregate defined for the table" $ do
r <- request methodGet "/lines?id=eq.1" (acceptHdrs "application/vnd.twkb") ""
@@ -32,7 +33,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
[json| {"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: text/plain"} |]
{ matchStatus = 406
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ matchContentTypeJson
, "Content-Length" <:> "110" ]
}
it "can get raw xml output with Accept: text/xml if there's an aggregate defined" $ do
@@ -40,7 +42,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
"<myxml>foo</myxml>bar<foobar><baz/></foobar>"
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
, matchHeaders = [ "Content-Type" <:> "text/xml; charset=utf-8"
, "Content-Length" <:> "44"]
}
-- TODO SOH (start of heading) is being added to results
@@ -50,7 +53,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
"\SOH{\"type\": \"FeatureCollection\", \"hello\": \"world\"}"
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/vnd.geo2+json"]
, matchHeaders = [ "Content-Type" <:> "application/vnd.geo2+json"
, "Content-Length" <:> "48" ]
}
it "will use the more specific application/vnd.geo2 handler for this table" $ do
@@ -83,14 +87,16 @@ spec = describe "custom media types" $ do
|</html>
|]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "text/html"]
, matchHeaders = [ "Content-Type" <:> "text/html"
, "Content-Length" <:> "117" ]
}
it "can get raw output with Accept: text/plain" $ do
request methodGet "/rpc/welcome" (acceptHdrs "text/plain") ""
`shouldRespondWith` "Welcome to PostgREST"
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "text/plain; charset=utf-8"]
, matchHeaders = [ "Content-Type" <:> "text/plain; charset=utf-8"
, "Content-Length" <:> "20" ]
}
it "can get raw xml output with Accept: text/xml" $ do
@@ -98,7 +104,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
"<my-xml-tag/>"
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
, matchHeaders = [ "Content-Type" <:> "text/xml; charset=utf-8"
, "Content-Length" <:> "13" ]
}
it "can get raw xml output with Accept: text/xml" $ do
@@ -132,6 +139,7 @@ spec = describe "custom media types" $ do
liftIO $ do
simpleBody r `shouldBe` readFixtureFile "A.png"
simpleHeaders r `shouldContain` [("Content-Type", "image/png")]
simpleHeaders r `shouldContain` [("Content-Length", "138")]
context "Proc that returns set of scalars and Accept: text/plain" $
it "will err because only scalars work with media type domains" $ do
@@ -141,7 +149,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
[json|{"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: text/plain"}|]
{ matchStatus = 406
, matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"]
, matchHeaders = [ "Content-Type" <:> "application/json; charset=utf-8"
, "Content-Length" <:> "110"]
}
context "Proc that returns rows and accepts custom media type" $ do
@@ -173,7 +182,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
[json| {"overridden": "true"} |]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"]
, matchHeaders = [ "Content-Type" <:> "application/json; charset=utf-8"
, "Content-Length" <:> "22" ]
}
-- TODO SOH (start of heading) is being added to results
@@ -182,7 +192,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
"\SOH{\"crs\": {\"type\": \"name\", \"properties\": {\"name\": \"EPSG:4326\"}}, \"type\": \"FeatureCollection\", \"features\": [{\"type\": \"Feature\", \"geometry\": {\"type\": \"LineString\", \"coordinates\": [[1, 1], [5, 5]]}, \"properties\": {\"id\": 1, \"name\": \"line-1\"}}]}"
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/geo+json; charset=utf-8"]
, matchHeaders = [ "Content-Type" <:> "application/geo+json; charset=utf-8"
, "Content-Length" <:> "239" ]
}
it "will not override vendored media types like application/vnd.pgrst.object" $
@@ -190,7 +201,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
[json|{"id":1,"name":"Windows 7","client_id":1}|]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"]
, matchHeaders = [ "Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"
, "Content-Length" <:> "41" ]
}
context "matches requested media type correctly" $ do
@@ -200,11 +212,13 @@ spec = describe "custom media types" $ do
liftIO $ do
simpleBody r1 `shouldBe` readFixtureFile "A.png"
simpleHeaders r1 `shouldContain` [("Content-Type", "image/png")]
simpleHeaders r1 `shouldContain` [("Content-Length", "138")]
r2 <- request methodGet "/rpc/ret_image" (acceptHdrs "text/html,application/xhtml+xml,application/xml;q=0.9,image/png,*/*;q=0.8") ""
liftIO $ do
simpleBody r2 `shouldBe` readFixtureFile "A.png"
simpleHeaders r2 `shouldContain` [("Content-Type", "image/png")]
simpleHeaders r2 `shouldContain` [("Content-Length", "138")]
-- https://github.com/PostgREST/postgrest/issues/2170
it "will match json in presence of text/plain" $ do
@@ -219,7 +233,8 @@ spec = describe "custom media types" $ do
`shouldRespondWith`
"id\tname\tclient_id\n1\tWindows 7\t1\n2\tWindows 10\t1\n"
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "text/tab-separated-values"]
, matchHeaders = [ "Content-Type" <:> "text/tab-separated-values"
, "Content-Length" <:> "47" ]
}
-- https://github.com/PostgREST/postgrest/issues/1371#issuecomment-519248984
@@ -229,6 +244,7 @@ spec = describe "custom media types" $ do
simpleBody r `shouldBe` readFixtureFile "lines.csv"
simpleHeaders r `shouldContain` [("Content-Type", "text/csv; charset=utf-8")]
simpleHeaders r `shouldContain` [("Content-Disposition", "attachment; filename=\"lines.csv\"")]
simpleHeaders r `shouldContain` [("Content-Length", "216")]
-- https://github.com/PostgREST/postgrest/issues/3160
context "using select query parameter" $ do
@@ -239,7 +255,8 @@ spec = describe "custom media types" $ do
|(2,"Windows 10",1)
|]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "pg/outfunc"]
, matchHeaders = [ "Content-Type" <:> "pg/outfunc"
, "Content-Length" <:> "37" ]
}
it "with fewer columns selected" $ do
+9 -2
View File
@@ -22,6 +22,7 @@ spec =
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*" ]
}
@@ -30,6 +31,7 @@ spec =
`shouldRespondWith` [json|[{"id":2}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "*/1"
, "Content-Length" <:> "10"
, "Preference-Applied" <:> "return=representation, count=exact"]
}
@@ -41,6 +43,7 @@ spec =
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*" ]
}
request methodDelete "/items?id=eq.3&select=id"
@@ -50,6 +53,7 @@ spec =
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*"
, "Preference-Applied" <:> "return=minimal"]
}
@@ -58,7 +62,8 @@ spec =
request methodDelete "/complex_items?id=eq.2&select=id,name" [("Prefer", "return=representation")] ""
`shouldRespondWith` [json|[{"id":2,"name":"Two"}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "*/*"]
, matchHeaders = [ "Content-Range" <:> "*/*"
, "Content-Length" <:> "23" ]
}
it "can rename and cast the selected columns" $
@@ -137,6 +142,7 @@ spec =
""
{ matchStatus = 204
, matchHeaders = [matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Preference-Applied" <:> "return=minimal" ]
}
@@ -147,7 +153,8 @@ spec =
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [matchHeaderAbsent hContentType]
, matchHeaders = [matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength]
}
context "with ordering" $
@@ -40,7 +40,8 @@ spec =
}
|]
{ matchStatus = 300
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ matchContentTypeJson
, "Content-Length" <:> "828" ]
}
it "errs on an ambiguous embed that has a circular reference" $
+12 -6
View File
@@ -26,7 +26,8 @@ pgErrorCodeMapping = do
"details": null,
"hint": "Increase the configuration parameter \"max_stack_depth\" (currently 2048kB), after ensuring the platform's stack depth limit is adequate.",
"message": "stack depth limit exceeded"}|]
{ matchStatus = 500 }
{ matchStatus = 500
, matchHeaders = ["Content-Length" <:> "217"] }
context "includes the proxy-status header on the response" $ do
it "works with ApiRequest error" $
@@ -34,7 +35,8 @@ pgErrorCodeMapping = do
`shouldRespondWith`
[json| {"code":"PGRST125","details":null,"hint":null,"message":"Invalid path specified in request URL"} |]
{ matchStatus = 404
, matchHeaders = ["Proxy-Status" <:> "PostgREST; error=PGRST125"]
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST125"
, "Content-Length" <:> "96" ]
}
it "works with SchemaCache error" $
@@ -42,7 +44,8 @@ pgErrorCodeMapping = do
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.json_table'","message":"Could not find the table 'test.non_existent_table' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = ["Proxy-Status" <:> "PostgREST; error=PGRST205"]
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205"
, "Content-Length" <:> "172" ]
}
it "works with Jwt error" $ do
@@ -51,7 +54,8 @@ pgErrorCodeMapping = do
`shouldRespondWith`
[json| {"message":"Expected 3 parts in JWT; got 2","code":"PGRST301","hint":null,"details":null} |]
{ matchStatus = 401
, matchHeaders = ["Proxy-Status" <:> "PostgREST; error=PGRST301"]
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST301"
, "Content-Length" <:> "89" ]
}
it "works with raise sqlstate custom error" $
@@ -59,7 +63,8 @@ pgErrorCodeMapping = do
`shouldRespondWith`
[json| {"code":"PT402","details":"Quota exceeded","hint":"Upgrade your plan","message":"Payment Required"} |]
{ matchStatus = 402
, matchHeaders = ["Proxy-Status" <:> "PostgREST; error=PT402"]
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PT402"
, "Content-Length" <:> "99" ]
}
it "works with sqlstate PGRST custom error" $
@@ -67,5 +72,6 @@ pgErrorCodeMapping = do
`shouldRespondWith`
[json| {"code":"123","details":"DEF","hint":"XYZ","message":"ABC"} |]
{ matchStatus = 332
, matchHeaders = ["Proxy-Status" <:> "PostgREST; error=123"]
, matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=123"
, "Content-Length" <:> "59" ]
}
+14 -7
View File
@@ -30,7 +30,8 @@ spec actualPgVersion = do
} |] `shouldRespondWith` ""
{ matchStatus = 201
-- should not have content type set when body is empty
, matchHeaders = [matchHeaderAbsent hContentType]
, matchHeaders = [ matchHeaderAbsent hContentType
, "Content-Length" <:> "0"]
}
it "filters columns in result using &select" $
@@ -42,6 +43,7 @@ spec actualPgVersion = do
}] |] `shouldRespondWith` [json|[{"integer":14,"varchar":"testing!"}]|]
{ matchStatus = 201
, matchHeaders = [matchContentTypeJson
, "Content-Length" <:> "37"
, "Preference-Applied" <:> "return=representation"]
}
@@ -187,7 +189,8 @@ spec actualPgVersion = do
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType ]
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength ]
}
request methodPost "/auto_incrementing_pk"
@@ -211,7 +214,7 @@ spec actualPgVersion = do
[json|{"hint":null,"details":"Failing row contains (null, foo).","code":"23502","message":"null value in column \"k\" violates not-null constraint"}|]
)
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ matchContentTypeJson]
}
context "into a table with no pk" $ do
@@ -286,7 +289,8 @@ spec actualPgVersion = do
`shouldRespondWith`
[json|{"message":"Empty or invalid json","code":"PGRST102","details":null,"hint":null}|]
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ matchContentTypeJson,
"Content-Length" <:> "80" ]
}
context "with no payload" $
@@ -370,7 +374,8 @@ spec actualPgVersion = do
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType ]
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength ]
}
request methodPost "/items2"
@@ -388,7 +393,8 @@ spec actualPgVersion = do
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType ]
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength ]
}
request methodPost "/items3?select=id"
@@ -623,7 +629,8 @@ spec actualPgVersion = do
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [matchHeaderAbsent hContentType]
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength ]
}
describe "CSV insert" $ do
+10
View File
@@ -33,6 +33,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` (if actualPgVersion >= pgVersion170 then 11.32 else 15.63)
@@ -140,6 +141,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 0.06
@@ -153,6 +155,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 8.23
@@ -166,6 +169,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` (if actualPgVersion >= pgVersion170 then 11.37 else 15.68)
@@ -180,6 +184,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.55
@@ -194,6 +199,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 5.53
@@ -248,6 +254,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/vnd.pgrst.object+json\"; options=verbose; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
aggCol `shouldBe` Just [aesonQQ| "COALESCE((json_agg(ROW(projects.id, projects.name, projects.client_id)) -> 0), 'null'::json)" |]
describe "function plan" $ do
@@ -261,6 +268,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 68.56
@@ -275,6 +283,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+text; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
resBody `shouldSatisfy` (\t -> LBS.take 9 t == "Aggregate")
@@ -288,6 +297,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+text; for=\"application/json\"; charset=utf-8")
resHeaders `shouldSatisfy` notZeroContentLength
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
resBody `shouldSatisfy` (\t -> LBS.take 9 t == "Aggregate")
+45 -15
View File
@@ -28,14 +28,17 @@ spec = do
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.private_table'","message":"Could not find the table 'test.faketable' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = []
, matchHeaders = ["Content-Length" <:> "166"]
}
describe "Filtering response" $ do
it "matches with equality" $
get "/items?id=eq.5"
`shouldRespondWith` [json| [{"id":5}] |]
{ matchHeaders = ["Content-Range" <:> "0-0/*"] }
{ matchHeaders = [
"Content-Range" <:> "0-0/*",
"Content-Length" <:> "10"
] }
it "matches with equality using not operator" $
get "/items?id=not.eq.5&order=id"
@@ -511,7 +514,10 @@ spec = do
"code":"PGRST108",
"message":"'non_existent_projects' is not an embedded resource in this request"}|]
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [
"Content-Length" <:> "204",
matchContentTypeJson
]
}
get "/clients?select=*,amiga_projects:projects(*)&amiga_projectsss.name=ilike.*Amiga*" `shouldRespondWith`
[json|
@@ -590,7 +596,7 @@ spec = do
get "/complex_items?select=id::fakecolumntype"
`shouldRespondWith` [json| {"hint":null,"details":null,"code":"42704","message":"type \"fakecolumntype\" does not exist"} |]
{ matchStatus = 400
, matchHeaders = []
, matchHeaders = ["Content-Length" <:> "94"]
}
it "can cast types with underscore and numbers" $
@@ -603,7 +609,11 @@ spec = do
it "requesting parents and children" $
get "/projects?id=eq.1&select=id, name, clients(*), tasks(id, name)" `shouldRespondWith`
[json|[{"id":1,"name":"Windows 7","clients":{"id":1,"name":"Microsoft"},"tasks":[{"id":1,"name":"Design w7"},{"id":2,"name":"Code w7"}]}]|]
{ matchHeaders = [matchContentTypeJson] }
{ matchHeaders = [
"Content-Length" <:> "141",
matchContentTypeJson
]
}
it "requesting parent and renaming primary key" $
get "/projects?select=name,client:clients(clientId:id,name)" `shouldRespondWith`
@@ -1145,7 +1155,10 @@ spec = do
get "/items?order=id.asc.nullslasttt" `shouldRespondWith`
[json|{"details":"unexpected 't' expecting \",\" or end of input","message":"\"failed to parse order (id.asc.nullslasttt)\" (line 1, column 17)","code":"PGRST100","hint":null}|]
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [
"Content-Length" <:> "169",
matchContentTypeJson
]
}
describe "Accept headers" $ do
@@ -1155,7 +1168,10 @@ spec = do
`shouldRespondWith`
[json|{"message":"None of these media types are available: text/unknowntype","code":"PGRST107","details":null,"hint":null}|]
{ matchStatus = 406
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [
"Content-Length" <:> "116",
matchContentTypeJson
]
}
it "should respond correctly to */* in accept header" $
@@ -1194,7 +1210,10 @@ spec = do
(acceptHdrs "text/csv; version=1") ""
`shouldRespondWith` "k,extra\nxyyx,u\nxYYx,v"
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "text/csv; charset=utf-8"]
, matchHeaders = [
"Content-Type" <:> "text/csv; charset=utf-8",
"Content-Length" <:> "21"
]
}
describe "Canonical location" $ do
@@ -1512,7 +1531,10 @@ spec = do
get "/datarep_todos?select=id,label_color,banana" `shouldRespondWith`
[json| {"code":"42703","details":null,"hint":null,"message":"column datarep_todos.banana does not exist"} |]
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [
"Content-Length" <:> "98",
matchContentTypeJson
]
}
it "formats columns in views including computed columns" $
get "/datarep_todos_computed?select=id,label_color,dark_color" `shouldRespondWith`
@@ -1548,8 +1570,8 @@ spec = do
get "/datarep_todos?id=lt.4" `shouldRespondWith`
[json| [
{"id":1,"name":"Report","label_color":"#000000","due_at":"2018-01-02T00:00:00Z","icon_image":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAAABBJREFUeJxiYAEAAAAA//8DAAAABgAFBXv6vUAAAAAASUVORK5CYII=","created_at":1513213350,"budget":"12.50"},
{"id":2,"name":"Essay","label_color":"#000100","due_at":"2018-01-03T00:00:00Z","icon_image":null,"created_at":1513213350,"budget":"100000000000000.13"},
{"id":3,"name":"Algebra","label_color":"#01E240","due_at":"2018-01-01T14:12:34.123456Z","icon_image":null,"created_at":1513213350,"budget":"0.00"}
{"id":2,"name":"Essay","label_color":"#000100","due_at":"2018-01-03T00:00:00Z","icon_image":null,"created_at":1513213350,"budget":"100000000000000.13"},
{"id":3,"name":"Algebra","label_color":"#01E240","due_at":"2018-01-01T14:12:34.123456Z","icon_image":null,"created_at":1513213350,"budget":"0.00"}
] |]
{ matchHeaders = [matchContentTypeJson] }
it "formats star and explicit mix" $
@@ -1580,7 +1602,10 @@ spec = do
-- we prove the parser is not used because it'd replace the Z with `+00:00` and a different error message.
[json| {"code":"22007","details":null,"hint":null,"message":"invalid input syntax for type timestamp with time zone: \"Z\""} |]
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [
"Content-Length" <:> "117",
matchContentTypeJson
]
}
it "uses text parser for filter with 'IN' predicates" $
get "/datarep_todos?select=id,due_at&label_color=in.(000100,01E240)" `shouldRespondWith`
@@ -1607,7 +1632,10 @@ spec = do
{"code":"42883","details":null,"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"}
|]
{ matchStatus = 404
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [
"Content-Length" <:> "200",
matchContentTypeJson
]
}
context "searching for an empty string" $ do
@@ -1624,11 +1652,13 @@ spec = do
it "return http status 500" $
get "/infinite_recursion?select=*" `shouldRespondWith`
[json|{"code":"42P17","message":"infinite recursion detected in rules for relation \"infinite_recursion\"","details":null,"hint":null}|]
{ matchStatus = 500 }
{ matchStatus = 500
, matchHeaders = ["Content-Length" <:> "128"] }
context "invalid resource path" $ do
it "return http status 404" $
get "/first/second/third?select=*"
`shouldRespondWith`
[json| {"code":"PGRST125","details":null,"hint":null,"message":"Invalid path specified in request URL"} |]
{ matchStatus = 404 }
{ matchStatus = 404
, matchHeaders = ["Content-Length" <:> "96"]}
+8 -4
View File
@@ -22,7 +22,10 @@ spec = do
context "when I don't want the count" $ do
it "returns range Content-Range with */* for empty range" $
get "/rpc/getitemrange?min=2&max=2"
`shouldRespondWith` [json| [] |] {matchHeaders = ["Content-Range" <:> "*/*"]}
`shouldRespondWith` [json| [] |]
{ matchHeaders = [ "Content-Range" <:> "*/*"
, "Content-Length" <:> "2" ]
}
it "returns range Content-Range with range/*" $
get "/rpc/getitemrange?order=id&min=0&max=15"
@@ -42,7 +45,8 @@ spec = do
"hint":null
}|]
{ matchStatus = 416
, matchHeaders = ["Content-Range" <:> "*/0"]
, matchHeaders = [ "Content-Range" <:> "*/0"
, "Content-Length" <:> "144"]
}
it "refuses a range requesting start past last item" $
@@ -65,8 +69,8 @@ spec = do
r <- request methodGet "/rpc/getitemrange?min=0&max=15"
(rangeHdrs $ ByteRangeFromTo 0 1) mempty
liftIO $ do
simpleHeaders r `shouldSatisfy`
matchHeader "Content-Range" "0-1/*"
simpleHeaders r `shouldSatisfy` matchHeader "Content-Range" "0-1/*"
simpleHeaders r `shouldSatisfy` matchHeader "Content-Length" "22"
simpleStatus r `shouldBe` ok200
it "understands open-ended ranges" $
+42 -16
View File
@@ -31,18 +31,21 @@ spec =
post "/rpc/getitemrange?limit=1&offset=1" [json| { "min": 2, "max": 4 } |]
`shouldRespondWith` [json| [{"id":4}] |]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "1-1/*"]
, matchHeaders = [ "Content-Range" <:> "1-1/*"
, "Content-Length" <:> "10" ]
}
get "/rpc/getitemrange?min=2&max=4&limit=1&offset=1"
`shouldRespondWith` [json| [{"id":4}] |]
{ matchStatus = 200
, matchHeaders = ["Content-Range" <:> "1-1/*"]
, matchHeaders = [ "Content-Range" <:> "1-1/*"
, "Content-Length" <:> "10"]
}
request methodHead "/rpc/getitemrange?min=2&max=4&limit=1&offset=1" mempty mempty
`shouldRespondWith`
""
{ matchStatus = 200
, matchHeaders = [ matchContentTypeJson
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "1-1/*" ]
}
@@ -210,7 +213,8 @@ spec =
"code":"PGRST202",
"details":"Searched for the function test.sayhell without parameters, but no matches were found in the schema cache."} |]
{ matchStatus = 404
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ "Content-Length" <:> "291"
, matchContentTypeJson ]
}
it "should fail with 404 on unknown proc args" $ do
@@ -268,7 +272,8 @@ spec =
"code":"PGRST203",
"details":null} |]
{ matchStatus = 300
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ "Content-Length" <:> "353"
, matchContentTypeJson ]
}
it "works when having uppercase identifiers" $ do
@@ -465,7 +470,8 @@ spec =
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [matchHeaderAbsent hContentType]
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength]
}
it "returns null for an integer with null value" $
@@ -613,7 +619,8 @@ spec =
`shouldRespondWith`
[json|{"message":"Cannot use the DELETE method on RPC","code":"PGRST101","details":null,"hint":null}|]
{ matchStatus = 405
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ "Content-Length" <:> "94"
, matchContentTypeJson ]
}
it "PATCH fails" $
request methodPatch "/rpc/sayhello" [] ""
@@ -628,7 +635,8 @@ spec =
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType ]
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength]
}
-- now the test
@@ -761,7 +769,8 @@ spec =
get "/rpc/raise_pt402"
`shouldRespondWith` [json|{ "hint": "Upgrade your plan", "details": "Quota exceeded", "code": "PT402", "message": "Payment Required" }|]
{ matchStatus = 402
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ "Content-Length" <:> "99"
, matchContentTypeJson ]
}
it "defaults to status 500 if RAISE code is PT not followed by a number" $
@@ -769,7 +778,8 @@ spec =
`shouldRespondWith`
[json|{"hint": null, "details": null, "code": "PT40A", "message": "Wrong"}|]
{ matchStatus = 500
, matchHeaders = [ matchContentTypeJson ]
, matchHeaders = [ "Content-Length" <:> "61"
, matchContentTypeJson ]
}
context "should work with an overloaded function" $ do
@@ -1024,7 +1034,8 @@ spec =
`shouldRespondWith`
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value","code":"PGRST111","details":null,"hint":null}|]
{ matchStatus = 500
, matchHeaders = [ matchContentTypeJson ]
, matchHeaders = [ "Content-Length" <:> "157"
, matchContentTypeJson ]
}
get "/rpc/bad_guc_headers_2"
`shouldRespondWith`
@@ -1051,6 +1062,7 @@ spec =
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Set-Cookie" <:> "sessionid=38afes7a8; HttpOnly; Path=/"
, "Set-Cookie" <:> "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly" ]
}
@@ -1113,7 +1125,8 @@ spec =
`shouldRespondWith`
[json|{"message":"response.status guc must be a valid status code","code":"PGRST112","details":null,"hint":null}|]
{ matchStatus = 500
, matchHeaders = [ matchContentTypeJson ]
, matchHeaders = [ "Content-Length" <:> "106"
, matchContentTypeJson ]
}
context "single unnamed param" $ do
@@ -1291,7 +1304,8 @@ spec =
"details":"line 1: StartTag: invalid element name\n<\n ^"
}|]
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson]
, matchHeaders = [ "Content-Length" <:> "118"
, matchContentTypeJson ]
}
-- https://github.com/PostgREST/postgrest/issues/1586#issuecomment-696345442
@@ -1304,7 +1318,10 @@ spec =
post "/rpc/char_param_insert" [json| { "char_": "abcdefg", "char_arr": "{abc,abcdefg}" } |]
`shouldRespondWith`
[json| {"code":"22001","details":null,"hint":null,"message":"value too long for type character(5)"} |]
{ matchStatus = 400 }
{ matchStatus = 400
, matchHeaders = [ "Content-Length" <:> "92"
, matchContentTypeJson ]
}
it "modifies the param type from bit to bit varying" $ do
get "/rpc/bit_param_select?bit_=101010&bit_arr={101,101010}" `shouldRespondWith`
@@ -1314,7 +1331,10 @@ spec =
post "/rpc/bit_param_insert" [json| { "bit_": "101010", "bit_arr": "{101,101010}" } |]
`shouldRespondWith`
[json| {"code":"22026","details":null,"hint":null,"message":"bit string length 6 does not match type bit(5)"} |]
{ matchStatus = 400 }
{ matchStatus = 400
, matchHeaders = [ "Content-Length" <:> "102"
, matchContentTypeJson ]
}
context "get message and details from raise sqlstate" $ do
it "gets message and details from raise sqlstate PGRST" $ do
@@ -1369,7 +1389,10 @@ spec =
"message":"Could not parse JSON in the \"RAISE SQLSTATE 'PGRST'\" error",
"details":"Invalid JSON value for MESSAGE: 'INVALID'",
"hint":"MESSAGE must be a JSON object with obligatory keys: 'code', 'message' and optional keys: 'details', 'hint'."}|]
{ matchStatus = 500 }
{ matchStatus = 500
, matchHeaders = [ "Content-Length" <:> "263"
, matchContentTypeJson ]
}
it "returns error for invalid JSON in the DETAIL option of the RAISE statement" $
get "/rpc/raise_sqlstate_invalid_json_details" `shouldRespondWith`
@@ -1395,4 +1418,7 @@ spec =
it "should return http status 500" $
request methodGet "/rpc/temp_file_limit" [auth] "" `shouldRespondWith`
[json|{"code":"53400","message":"temporary file size exceeds temp_file_limit (1kB)","details":null,"hint":null}|]
{ matchStatus = 500 }
{ matchStatus = 500
, matchHeaders = [ "Content-Length" <:> "105"
, matchContentTypeJson ]
}
+25 -7
View File
@@ -20,7 +20,7 @@ spec = do
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.factories'","message":"Could not find the table 'test.fake' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = []
, matchHeaders = ["Content-Length" <:> "157"]
}
@@ -31,7 +31,8 @@ spec = do
`shouldRespondWith`
""
{ matchStatus = 204,
matchHeaders = [matchHeaderAbsent hContentType]
matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength ]
}
context "with invalid json payload" $
@@ -40,7 +41,8 @@ spec = do
`shouldRespondWith`
[json|{"message":"Empty or invalid json","code":"PGRST102","details":null,"hint":null}|]
{ matchStatus = 400,
matchHeaders = [matchContentTypeJson]
matchHeaders = [ matchContentTypeJson
, "Content-Length" <:> "80"]
}
context "with no payload" $
@@ -59,6 +61,7 @@ spec = do
"\t \n \r { \"id\": 99 } \t \n \r "
`shouldRespondWith` [json|[{"id":99}]|]
{ matchStatus = 200
, matchHeaders = ["Content-Length" <:> "11"]
}
context "in a nonempty table" $ do
@@ -69,6 +72,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "0-0/*" ]
}
@@ -78,7 +82,8 @@ spec = do
`shouldRespondWith` "[]"
{
matchStatus = 200,
matchHeaders = ["Preference-Applied" <:> "return=representation"]
matchHeaders = [ "Preference-Applied" <:> "return=representation"
, "Content-Length" <:> "2"]
}
it "returns status code 200 when no rows updated" $
@@ -107,6 +112,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "0-1/*"
, "Preference-Applied" <:> "tx=commit" ]
}
@@ -123,7 +129,8 @@ spec = do
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [matchHeaderAbsent hContentType]
, matchHeaders = [matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength ]
}
it "can set a column to NULL" $ do
@@ -207,6 +214,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "0-0/*"
, "Preference-Applied" <:> "return=minimal"]
}
@@ -217,6 +225,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "0-0/*"
, "Preference-Applied" <:> "return=minimal"]
}
@@ -230,6 +239,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*" ]
}
@@ -240,6 +250,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*" ]
}
@@ -250,6 +261,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*" ]
}
@@ -261,6 +273,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*" ]
}
@@ -271,6 +284,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*" ]
}
@@ -281,6 +295,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "*/*" ]
}
@@ -350,7 +365,7 @@ spec = do
`shouldRespondWith`
[json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.articles'","message":"Could not find the table 'test.garlic' in the schema cache"} |]
{ matchStatus = 404
, matchHeaders = []
, matchHeaders = ["Content-Length" <:> "158"]
}
context "apply defaults on missing values" $ do
@@ -613,6 +628,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Preference-Applied" <:> "return=minimal"]
}
@@ -623,7 +639,8 @@ spec = do
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [matchHeaderAbsent hContentType]
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength ]
}
-- Data representations for payload parsing requires Postgres 10 or above.
@@ -636,6 +653,7 @@ spec = do
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "Content-Range" <:> "0-0/*" ]
}
+3 -2
View File
@@ -30,7 +30,9 @@ deleteItems =
`shouldRespondWith`
""
{ matchStatus = 204
, matchHeaders = [matchHeaderAbsent hContentType] }
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength ]
}
preferDefault = [("Prefer", "return=representation")]
preferCommit = [("Prefer", "return=representation"), ("Prefer", "tx=commit")]
@@ -262,4 +264,3 @@ forced = describe "tx-rollback-all = true, tx-allow-override = false" $ do
preferRollback `shouldRespondToReads` withoutPreferenceApplied
preferRollback `shouldNotPersistMutations` withoutPreferenceApplied
preferRollback `shouldRaiseExceptions` withoutPreferenceApplied
@@ -56,6 +56,7 @@ spec =
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "X-Custom-Header" <:> "mykey=myval" ]
}
@@ -66,6 +67,7 @@ spec =
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "X-Custom-Header" <:> "mykey=myval" ]
}
@@ -75,6 +77,7 @@ spec =
""
{ matchStatus = 204
, matchHeaders = [ matchHeaderAbsent hContentType
, matchHeaderAbsent hContentLength
, "X-Custom-Header" <:> "mykey=myval" ]
}
it "can override the Content-Type header" $ do
+3
View File
@@ -281,6 +281,9 @@ noBlankHeader = notElem mempty
noProfileHeader :: [Header] -> Bool
noProfileHeader headers = isNothing $ find ((== "Content-Profile") . fst) headers
notZeroContentLength :: [Header] -> Bool
notZeroContentLength headers = maybe False (/= "0") $ lookup hContentLength headers
authHeader :: BS.ByteString -> BS.ByteString -> Header
authHeader typ creds =
(hAuthorization, typ <> " " <> creds)