feat: sql handlers for custom media types
* test text/html and drop HtmlRawOutputSpec.hs * all tests passing, removed all pendingWith * make functions compatible with pg <= 12 * move custom media types tests to own spec * anyelement aggregate * apply aggregates without a final function * overriding works * overriding anyelement with particular agg * cannot override vendored media types * plan spec works with custom aggregate * renamed media types to make clear which ones are overridable * correct content negotiation with same weight * text/tab-separated-values media type * text/csv with BOM plus content-disposition header
This commit is contained in:
committed by
Steve Chavez
parent
4a90e9fbd9
commit
82b38341bf
@@ -0,0 +1,224 @@
|
||||
module Feature.Query.CustomMediaSpec where
|
||||
|
||||
import Network.Wai (Application)
|
||||
|
||||
import Network.HTTP.Types
|
||||
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
|
||||
import Test.Hspec
|
||||
import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
import Text.Heredoc (str)
|
||||
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
|
||||
spec :: SpecWith ((), Application)
|
||||
spec = describe "custom media types" $ do
|
||||
context "for tables with aggregate" $ do
|
||||
it "can query if there's an aggregate defined for the table" $ do
|
||||
r <- request methodGet "/lines" (acceptHdrs "application/vnd.twkb") ""
|
||||
liftIO $ do
|
||||
simpleBody r `shouldBe` readFixtureFile "lines.twkb"
|
||||
simpleHeaders r `shouldContain` [("Content-Type", "application/vnd.twkb")]
|
||||
|
||||
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") ""
|
||||
liftIO $ do
|
||||
simpleBody r `shouldBe` readFixtureFile "1.twkb"
|
||||
simpleHeaders r `shouldContain` [("Content-Type", "application/vnd.twkb")]
|
||||
|
||||
it "will fail if there's no aggregate defined for the table" $ do
|
||||
request methodGet "/lines" (acceptHdrs "text/plain") ""
|
||||
`shouldRespondWith`
|
||||
[json| {"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: text/plain"} |]
|
||||
{ matchStatus = 415
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml if there's an aggregate defined" $ do
|
||||
request methodGet "/xmltest" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<myxml>foo</myxml>bar<foobar><baz/></foobar>"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
|
||||
}
|
||||
|
||||
-- TODO SOH (start of heading) is being added to results
|
||||
context "for tables with anyelement aggregate" $ do
|
||||
it "will use the application/vnd.geo2+json media type for any table" $
|
||||
request methodGet "/lines" (acceptHdrs "application/vnd.geo2+json") ""
|
||||
`shouldRespondWith`
|
||||
"\SOH{\"type\": \"FeatureCollection\", \"hello\": \"world\"}"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/vnd.geo2+json"]
|
||||
}
|
||||
|
||||
it "will use the more specific application/vnd.geo2 handler for this table" $ do
|
||||
request methodGet "/shop_bles" (acceptHdrs "application/vnd.geo2+json") ""
|
||||
`shouldRespondWith`
|
||||
"\SOH\"anyelement overridden\""
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/vnd.geo2+json"]
|
||||
}
|
||||
|
||||
request methodGet "/rpc/get_shop_bles" (acceptHdrs "application/vnd.geo2+json") ""
|
||||
`shouldRespondWith`
|
||||
"\SOH\"anyelement overridden\""
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/vnd.geo2+json"]
|
||||
}
|
||||
|
||||
context "Proc that returns scalar" $ do
|
||||
it "can get raw output with Accept: text/html" $ do
|
||||
request methodGet "/rpc/welcome.html" (acceptHdrs "text/html") ""
|
||||
`shouldRespondWith`
|
||||
[str|
|
||||
|<html>
|
||||
| <head>
|
||||
| <title>PostgREST</title>
|
||||
| </head>
|
||||
| <body>
|
||||
| <h1>Welcome to PostgREST</h1>
|
||||
| </body>
|
||||
|</html>
|
||||
|]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/html"]
|
||||
}
|
||||
|
||||
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"]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml" $ do
|
||||
request methodGet "/rpc/return_scalar_xml" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<my-xml-tag/>"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml" $ do
|
||||
request methodGet "/rpc/welcome.xml" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<html>\n <head>\n <title>PostgREST</title>\n </head>\n <body>\n <h1>Welcome to PostgREST</h1>\n </body>\n</html>"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "should fail with function returning text and Accept: text/xml" $ do
|
||||
request methodGet "/rpc/welcome" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
[json|
|
||||
{"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: text/xml"}
|
||||
|]
|
||||
{ matchStatus = 415
|
||||
, matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"]
|
||||
}
|
||||
|
||||
context "Proc that returns scalar based on a table" $ do
|
||||
it "can get an image with Accept: image/png" $ do
|
||||
r <- request methodGet "/rpc/ret_image" (acceptHdrs "image/png") ""
|
||||
liftIO $ do
|
||||
simpleBody r `shouldBe` readFixtureFile "A.png"
|
||||
simpleHeaders r `shouldContain` [("Content-Type", "image/png")]
|
||||
|
||||
context "Proc that returns set of scalars and Accept: text/plain" $
|
||||
it "will err because only scalars work with media type domains" $ do
|
||||
request methodGet "/rpc/welcome_twice"
|
||||
(acceptHdrs "text/plain")
|
||||
""
|
||||
`shouldRespondWith`
|
||||
[json|{"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: text/plain"}|]
|
||||
{ matchStatus = 415
|
||||
, matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"]
|
||||
}
|
||||
|
||||
context "Proc that returns rows and accepts custom media type" $ do
|
||||
it "works if it has an aggregate defined" $ do
|
||||
r <- request methodGet "/rpc/get_lines" [("Accept", "application/vnd.twkb")] ""
|
||||
liftIO $ do
|
||||
simpleBody r `shouldBe` readFixtureFile "lines.twkb"
|
||||
simpleHeaders r `shouldContain` [("Content-Type", "application/vnd.twkb")]
|
||||
|
||||
it "fails if doesn't have an aggregate defined" $ do
|
||||
request methodGet "/rpc/get_lines"
|
||||
(acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith`
|
||||
[json| {"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: application/octet-stream"} |]
|
||||
{ matchStatus = 415 }
|
||||
|
||||
-- TODO SOH (start of heading) is being added to results
|
||||
it "works if there's an anyelement aggregate defined" $ do
|
||||
request methodGet "/rpc/get_lines" (acceptHdrs "application/vnd.geo2+json") ""
|
||||
`shouldRespondWith`
|
||||
"\SOH{\"type\": \"FeatureCollection\", \"hello\": \"world\"}"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/vnd.geo2+json"]
|
||||
}
|
||||
|
||||
context "overriding" $ do
|
||||
it "will override the application/json handler for a single table" $
|
||||
request methodGet "/ov_json" (acceptHdrs "application/json") ""
|
||||
`shouldRespondWith`
|
||||
[json| {"overridden": "true"} |]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"]
|
||||
}
|
||||
|
||||
-- TODO SOH (start of heading) is being added to results
|
||||
it "will override the application/geo+json handler for a single table" $
|
||||
request methodGet "/lines?id=eq.1" (acceptHdrs "application/geo+json") ""
|
||||
`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"]
|
||||
}
|
||||
|
||||
it "will not override vendored media types like application/vnd.pgrst.object" $
|
||||
request methodGet "/projects?id=eq.1" (acceptHdrs "application/vnd.pgrst.object") ""
|
||||
`shouldRespondWith`
|
||||
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"]
|
||||
}
|
||||
|
||||
context "matches requested media type correctly" $ do
|
||||
-- https://github.com/PostgREST/postgrest/issues/1462
|
||||
it "will match image/png according to q values" $ do
|
||||
r1 <- request methodGet "/rpc/ret_image" (acceptHdrs "image/png, */*") ""
|
||||
liftIO $ do
|
||||
simpleBody r1 `shouldBe` readFixtureFile "A.png"
|
||||
simpleHeaders r1 `shouldContain` [("Content-Type", "image/png")]
|
||||
|
||||
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")]
|
||||
|
||||
-- https://github.com/PostgREST/postgrest/issues/2170
|
||||
it "will match json in presence of text/plain" $ do
|
||||
r <- request methodGet "/projects?id=eq.1" (acceptHdrs "text/plain, application/json") ""
|
||||
liftIO $ do
|
||||
simpleStatus r `shouldBe` status200
|
||||
simpleHeaders r `shouldContain` [("Content-Type", "application/json; charset=utf-8")]
|
||||
|
||||
-- https://github.com/PostgREST/postgrest/issues/1102
|
||||
it "will match a custom text/tab-separated-values" $ do
|
||||
request methodGet "/projects?id=in.(1,2)" (acceptHdrs "text/tab-separated-values") ""
|
||||
`shouldRespondWith`
|
||||
"id\tname\tclient_id\n1\tWindows 7\t1\n2\tWindows 10\t1\n"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/tab-separated-values"]
|
||||
}
|
||||
|
||||
-- https://github.com/PostgREST/postgrest/issues/1371#issuecomment-519248984
|
||||
it "will match a custom text/csv with BOM" $ do
|
||||
r <- request methodGet "/lines" (acceptHdrs "text/csv") ""
|
||||
liftIO $ 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\"")]
|
||||
@@ -1,31 +0,0 @@
|
||||
module Feature.Query.HtmlRawOutputSpec where
|
||||
|
||||
import Network.Wai (Application)
|
||||
|
||||
import Network.HTTP.Types
|
||||
import Test.Hspec hiding (pendingWith)
|
||||
import Test.Hspec.Wai
|
||||
import Text.Heredoc
|
||||
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper (acceptHdrs)
|
||||
|
||||
spec :: SpecWith ((), Application)
|
||||
spec = describe "When raw-media-types is set to \"text/html\"" $
|
||||
it "can get raw output with Accept: text/html" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome.html" (acceptHdrs "text/html") ""
|
||||
`shouldRespondWith`
|
||||
[str|
|
||||
|<html>
|
||||
| <head>
|
||||
| <title>PostgREST</title>
|
||||
| </head>
|
||||
| <body>
|
||||
| <h1>Welcome to PostgREST</h1>
|
||||
| </body>
|
||||
|</html>
|
||||
|]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/html"]
|
||||
}
|
||||
@@ -275,18 +275,6 @@ spec actualPgVersion = do
|
||||
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
|
||||
totalCost `shouldBe` 68.56
|
||||
|
||||
it "outputs the plan for text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
r <- request methodGet "/rpc/return_scalar_xml"
|
||||
(acceptHdrs "application/vnd.pgrst.plan+json; for=\"text/xml\"; options=verbose") ""
|
||||
|
||||
let aggCol = simpleBody r ^? nth 0 . key "Plan" . key "Output" . nth 2
|
||||
resHeaders = simpleHeaders r
|
||||
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"text/xml\"; options=verbose; charset=utf-8")
|
||||
aggCol `shouldBe` Just [aesonQQ| "COALESCE(xmlagg(return_scalar_xml.pgrst_scalar), ''::xml)" |]
|
||||
|
||||
describe "text format" $ do
|
||||
it "outputs the total cost for a function call" $ do
|
||||
r <- request methodGet "/projects?id=in.(1,2,3)"
|
||||
@@ -442,6 +430,34 @@ spec actualPgVersion = do
|
||||
liftIO $ do
|
||||
resBody `shouldSatisfy` (\t -> T.isInfixOf "Index" (decodeUtf8 $ LBS.toStrict t))
|
||||
|
||||
describe "custom media types" $ do
|
||||
it "outputs the plan for a scalar function text/xml" $ do
|
||||
r <- request methodGet "/rpc/return_scalar_xml"
|
||||
(acceptHdrs "application/vnd.pgrst.plan+json; for=\"text/xml\"; options=verbose") ""
|
||||
|
||||
let aggCol = simpleBody r ^? nth 0 . key "Plan" . key "Output" . nth 2
|
||||
resHeaders = simpleHeaders r
|
||||
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"text/xml\"; options=verbose; charset=utf-8")
|
||||
aggCol `shouldBe` Just [aesonQQ| "return_scalar_xml.pgrst_scalar" |]
|
||||
|
||||
it "outputs the plan for an aggregate application/vnd.twkb" $ do
|
||||
r <- request methodGet "/lines"
|
||||
(acceptHdrs "application/vnd.pgrst.plan+json; for=\"application/vnd.twkb\"; options=verbose") ""
|
||||
|
||||
let aggCol = simpleBody r ^? nth 0 . key "Plan" . key "Output" . nth 2
|
||||
resHeaders = simpleHeaders r
|
||||
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/vnd.twkb\"; options=verbose; charset=utf-8")
|
||||
aggCol `shouldBe`
|
||||
(
|
||||
if actualPgVersion >= pgVersion120
|
||||
then Just [aesonQQ| "twkb_agg(ROW(lines.id, lines.name, lines.geom)::lines)" |]
|
||||
else Just [aesonQQ| "twkb_agg(ROW(pgrst_source.id, pgrst_source.name, pgrst_source.geom)::lines)" |]
|
||||
)
|
||||
|
||||
disabledSpec :: SpecWith ((), Application)
|
||||
disabledSpec =
|
||||
it "doesn't work if db-plan-enabled=false(the default)" $ do
|
||||
|
||||
@@ -1042,61 +1042,6 @@ spec actualPgVersion = do
|
||||
[json|[{"a$num$":100}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "binary output" $ do
|
||||
it "can query if a single column is selected" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/images_base64?select=img&name=eq.A.png" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCC"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/octet-stream"]
|
||||
}
|
||||
|
||||
it "can get raw output with Accept: text/plain" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/projects?select=name&id=eq.1" (acceptHdrs "text/plain") ""
|
||||
`shouldRespondWith` "Windows 7"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/plain; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/xmltest?select=xml" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<myxml>foo</myxml>bar<foobar><baz/></foobar>"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "fails if a single column is not selected" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/images?select=img,name&name=eq.A.png" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith`
|
||||
[json| {"message":"application/octet-stream requested but more than one column was selected","code":"PGRST113","details":null,"hint":null} |]
|
||||
{ matchStatus = 406 }
|
||||
|
||||
request methodGet "/images?select=*&name=eq.A.png"
|
||||
(acceptHdrs "application/octet-stream")
|
||||
""
|
||||
`shouldRespondWith`
|
||||
[json| {"message":"application/octet-stream requested but more than one column was selected","code":"PGRST113","details":null,"hint":null} |]
|
||||
{ matchStatus = 406 }
|
||||
|
||||
request methodGet "/images?name=eq.A.png"
|
||||
(acceptHdrs "application/octet-stream")
|
||||
""
|
||||
`shouldRespondWith`
|
||||
[json| {"message":"application/octet-stream requested but more than one column was selected","code":"PGRST113","details":null,"hint":null} |]
|
||||
{ matchStatus = 406 }
|
||||
|
||||
it "concatenates results if more than one row is returned" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/images_base64?select=img&name=in.(A.png,B.png)" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEX///8AAP94wDzzAAAAL0lEQVQIW2NgwAb+HwARH0DEDyDxwAZEyGAhLODqHmBRzAcn5GAS///A1IF14AAA5/Adbiiz/0gAAAAASUVORK5CYII="
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/octet-stream"]
|
||||
}
|
||||
|
||||
describe "values with quotes in IN and NOT IN" $ do
|
||||
it "succeeds when only quoted values are present" $ do
|
||||
get "/w_or_wo_comma_names?name=in.(\"Hebdon, John\")" `shouldRespondWith`
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
module Feature.Query.RpcSpec where
|
||||
|
||||
import qualified Data.ByteString.Lazy as BL (empty, readFile)
|
||||
import qualified Data.ByteString.Lazy as BL (empty)
|
||||
|
||||
import Network.Wai (Application)
|
||||
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
|
||||
|
||||
import Network.HTTP.Types
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
import Test.Hspec hiding (pendingWith)
|
||||
import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
@@ -1077,87 +1076,6 @@ spec actualPgVersion =
|
||||
, matchHeaders = []
|
||||
}
|
||||
|
||||
context "binary output" $ do
|
||||
context "Proc that returns scalar" $ do
|
||||
it "can query without selecting column" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/ret_base64_bin" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCC"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/octet-stream"]
|
||||
}
|
||||
|
||||
it "can get raw output with Accept: text/plain" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome" (acceptHdrs "text/plain") ""
|
||||
`shouldRespondWith` "Welcome to PostgREST"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/plain; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/return_scalar_xml" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<my-xml-tag/>"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome.xml" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<html>\n <head>\n <title>PostgREST</title>\n </head>\n <body>\n <h1>Welcome to PostgREST</h1>\n </body>\n</html>"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "should fail with function returning text and Accept: text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
[json|
|
||||
{
|
||||
"hint":"No function matches the given name and argument types. You might need to add explicit type casts.",
|
||||
"details":null,
|
||||
"code":"42883",
|
||||
"message":"function xmlagg(text) does not exist"
|
||||
}
|
||||
|]
|
||||
{ matchStatus = 406
|
||||
, matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"]
|
||||
}
|
||||
|
||||
context "Proc that returns set of scalars" $
|
||||
it "can query without selecting column" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome_twice"
|
||||
(acceptHdrs "text/plain")
|
||||
""
|
||||
`shouldRespondWith`
|
||||
"Welcome to PostgRESTWelcome to PostgREST"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/plain; charset=utf-8"]
|
||||
}
|
||||
|
||||
context "Proc that returns rows" $ do
|
||||
it "can query if a single column is selected" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/ret_rows_with_base64_bin?select=img" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEX///8AAP94wDzzAAAAL0lEQVQIW2NgwAb+HwARH0DEDyDxwAZEyGAhLODqHmBRzAcn5GAS///A1IF14AAA5/Adbiiz/0gAAAAASUVORK5CYII="
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/octet-stream"]
|
||||
}
|
||||
|
||||
it "fails if a single column is not selected" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/ret_rows_with_base64_bin"
|
||||
(acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith`
|
||||
[json| {"message":"application/octet-stream requested but more than one column was selected","code":"PGRST113","details":null,"hint":null} |]
|
||||
{ matchStatus = 406 }
|
||||
|
||||
context "only for GET rpc" $ do
|
||||
it "should fail on mutating procs" $ do
|
||||
get "/rpc/callcounter" `shouldRespondWith` 405
|
||||
@@ -1327,7 +1245,6 @@ spec actualPgVersion =
|
||||
[json|{"A": 1, "B": 2, "C": 3}|]
|
||||
|
||||
it "can insert text directly" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/unnamed_text_param"
|
||||
[("Content-Type", "text/plain"), ("Accept", "text/plain")]
|
||||
[str|unnamed text arg|]
|
||||
@@ -1335,7 +1252,6 @@ spec actualPgVersion =
|
||||
[str|unnamed text arg|]
|
||||
|
||||
it "can insert xml directly" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/unnamed_xml_param"
|
||||
[("Content-Type", "text/xml"), ("Accept", "text/xml")]
|
||||
[str|<note><from>John</from><to>Jane</to><message>Remember me</message></note>|]
|
||||
@@ -1343,8 +1259,7 @@ spec actualPgVersion =
|
||||
[str|<note><from>John</from><to>Jane</to><message>Remember me</message></note>|]
|
||||
|
||||
it "can insert bytea directly" $ do
|
||||
pendingWith "TBD"
|
||||
let file = unsafePerformIO $ BL.readFile "test/spec/fixtures/image.png"
|
||||
let file = readFixtureFile "image.png"
|
||||
r <- request methodPost "/rpc/unnamed_bytea_param"
|
||||
[("Content-Type", "application/octet-stream"), ("Accept", "application/octet-stream")]
|
||||
file
|
||||
@@ -1397,10 +1312,9 @@ spec actualPgVersion =
|
||||
}
|
||||
|
||||
it "will err when no function with single unnamed bytea parameter exists and application/octet-stream is specified" $
|
||||
let file = unsafePerformIO $ BL.readFile "test/spec/fixtures/image.png" in
|
||||
request methodPost "/rpc/unnamed_int_param"
|
||||
[("Content-Type", "application/octet-stream")]
|
||||
file
|
||||
(readFixtureFile "image.png")
|
||||
`shouldRespondWith`
|
||||
[json|{
|
||||
"hint": null,
|
||||
@@ -1429,7 +1343,6 @@ spec actualPgVersion =
|
||||
}
|
||||
|
||||
it "should be able to fallback to the single unnamed parameter function when other overloaded functions are not found" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/overloaded_unnamed_param"
|
||||
[("Content-Type", "application/json")]
|
||||
[json|{"A": 1, "B": 2, "C": 3}|]
|
||||
@@ -1440,7 +1353,7 @@ spec actualPgVersion =
|
||||
[str|unnamed text arg|]
|
||||
`shouldRespondWith`
|
||||
[str|unnamed text arg|]
|
||||
let file = unsafePerformIO $ BL.readFile "test/spec/fixtures/image.png"
|
||||
let file = readFixtureFile "image.png"
|
||||
r <- request methodPost "/rpc/overloaded_unnamed_param"
|
||||
[("Content-Type", "application/octet-stream"), ("Accept", "application/octet-stream")]
|
||||
file
|
||||
@@ -1488,7 +1401,6 @@ spec actualPgVersion =
|
||||
}
|
||||
|
||||
it "should fail on /rpc/unnamed_xml_param when posting invalid xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/unnamed_xml_param"
|
||||
[("Content-Type", "text/xml"), ("Accept", "text/xml")]
|
||||
[str|<|]
|
||||
|
||||
Reference in New Issue
Block a user