tests: don't match JSON bodies literally
The change of hash function with hashable-1.3.1.0 changes object ordering in JSON output, causing some test failures: https://app.circleci.com/pipelines/github/PostgREST/postgrest/805/workflows/067844c9-9ce4-49e8-8790-315625ab309b/jobs/8309 e.g.: > expected: "[{\"b\":\"baz\",\"a\":\"bar\"}]" > but got: "[{\"a\":\"bar\",\"b\":\"baz\"}]" This changes the tests to not compare against the body literally, and instead use the ResponseMatcher instance from Test.Hspec.Wai.JSON.
This commit is contained in:
committed by
Steve Chavez
parent
5baec4819f
commit
3a466aea9a
+58
-55
@@ -2,7 +2,7 @@ module Feature.InsertSpec where
|
|||||||
|
|
||||||
import Data.List (lookup)
|
import Data.List (lookup)
|
||||||
import Network.Wai (Application)
|
import Network.Wai (Application)
|
||||||
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
|
import Network.Wai.Test (SResponse (simpleHeaders))
|
||||||
import Test.Hspec hiding (pendingWith)
|
import Test.Hspec hiding (pendingWith)
|
||||||
import Test.Hspec.Wai.Matcher (bodyEquals)
|
import Test.Hspec.Wai.Matcher (bodyEquals)
|
||||||
|
|
||||||
@@ -20,17 +20,16 @@ spec actualPgVersion = do
|
|||||||
describe "Posting new record" $ do
|
describe "Posting new record" $ do
|
||||||
context "disparate json types" $ do
|
context "disparate json types" $ do
|
||||||
it "accepts disparate json types" $ do
|
it "accepts disparate json types" $ do
|
||||||
p <- post "/menagerie"
|
post "/menagerie"
|
||||||
[json| {
|
[json| {
|
||||||
"integer": 13, "double": 3.14159, "varchar": "testing!"
|
"integer": 13, "double": 3.14159, "varchar": "testing!"
|
||||||
, "boolean": false, "date": "1900-01-01", "money": "$3.99"
|
, "boolean": false, "date": "1900-01-01", "money": "$3.99"
|
||||||
, "enum": "foo"
|
, "enum": "foo"
|
||||||
} |]
|
} |] `shouldRespondWith` ""
|
||||||
liftIO $ do
|
{ matchStatus = 201
|
||||||
simpleBody p `shouldBe` ""
|
-- should not have content type set when body is empty
|
||||||
simpleStatus p `shouldBe` created201
|
, matchHeaders = [matchHeaderAbsent hContentType]
|
||||||
-- should not have content type set when body is empty
|
}
|
||||||
lookup hContentType (simpleHeaders p) `shouldBe` Nothing
|
|
||||||
|
|
||||||
it "filters columns in result using &select" $
|
it "filters columns in result using &select" $
|
||||||
request methodPost "/menagerie?select=integer,varchar" [("Prefer", "return=representation")]
|
request methodPost "/menagerie?select=integer,varchar" [("Prefer", "return=representation")]
|
||||||
@@ -121,13 +120,13 @@ spec actualPgVersion = do
|
|||||||
|
|
||||||
context "from an html form" $
|
context "from an html form" $
|
||||||
it "accepts disparate json types" $ do
|
it "accepts disparate json types" $ do
|
||||||
p <- request methodPost "/menagerie"
|
request methodPost "/menagerie"
|
||||||
[("Content-Type", "application/x-www-form-urlencoded")]
|
[("Content-Type", "application/x-www-form-urlencoded")]
|
||||||
("integer=7&double=2.71828&varchar=forms+are+fun&" <>
|
("integer=7&double=2.71828&varchar=forms+are+fun&" <>
|
||||||
"boolean=false&date=1900-01-01&money=$3.99&enum=foo")
|
"boolean=false&date=1900-01-01&money=$3.99&enum=foo")
|
||||||
liftIO $ do
|
`shouldRespondWith`
|
||||||
simpleBody p `shouldBe` ""
|
""
|
||||||
simpleStatus p `shouldBe` created201
|
{ matchStatus = 201 }
|
||||||
|
|
||||||
context "with no pk supplied" $ do
|
context "with no pk supplied" $ do
|
||||||
context "into a table with auto-incrementing pk" $
|
context "into a table with auto-incrementing pk" $
|
||||||
@@ -162,37 +161,40 @@ spec actualPgVersion = do
|
|||||||
|
|
||||||
context "into a table with no pk" $ do
|
context "into a table with no pk" $ do
|
||||||
it "succeeds with 201 but no location header" $ do
|
it "succeeds with 201 but no location header" $ do
|
||||||
p <- post "/no_pk" [json| { "a":"foo", "b":"bar" } |]
|
post "/no_pk" [json| { "a":"foo", "b":"bar" } |]
|
||||||
liftIO $ do
|
`shouldRespondWith`
|
||||||
simpleBody p `shouldBe` ""
|
""
|
||||||
lookup hLocation (simpleHeaders p) `shouldBe` Nothing
|
{ matchStatus = 201
|
||||||
simpleStatus p `shouldBe` created201
|
, matchHeaders = [matchHeaderAbsent hLocation]
|
||||||
|
}
|
||||||
|
|
||||||
it "returns full details of inserted record if asked" $ do
|
it "returns full details of inserted record if asked" $ do
|
||||||
p <- request methodPost "/no_pk"
|
request methodPost "/no_pk"
|
||||||
[("Prefer", "return=representation")]
|
[("Prefer", "return=representation")]
|
||||||
[json| { "a":"bar", "b":"baz" } |]
|
[json| { "a":"bar", "b":"baz" } |]
|
||||||
liftIO $ do
|
`shouldRespondWith`
|
||||||
simpleBody p `shouldBe` [json| [{ "a":"bar", "b":"baz" }] |]
|
[json| [{ "a":"bar", "b":"baz" }] |]
|
||||||
lookup hLocation (simpleHeaders p) `shouldBe` Nothing
|
{ matchStatus = 201
|
||||||
simpleStatus p `shouldBe` created201
|
, matchHeaders = [matchHeaderAbsent hLocation]
|
||||||
|
}
|
||||||
|
|
||||||
it "returns empty array when no items inserted, and return=rep" $ do
|
it "returns empty array when no items inserted, and return=rep" $ do
|
||||||
p <- request methodPost "/no_pk"
|
request methodPost "/no_pk"
|
||||||
[("Prefer", "return=representation")]
|
[("Prefer", "return=representation")]
|
||||||
[json| [] |]
|
[json| [] |]
|
||||||
liftIO $ do
|
`shouldRespondWith`
|
||||||
simpleBody p `shouldBe` [json| [] |]
|
[json| [] |]
|
||||||
simpleStatus p `shouldBe` created201
|
{ matchStatus = 201 }
|
||||||
|
|
||||||
it "can post nulls" $ do
|
it "can post nulls" $ do
|
||||||
p <- request methodPost "/no_pk"
|
request methodPost "/no_pk"
|
||||||
[("Prefer", "return=representation")]
|
[("Prefer", "return=representation")]
|
||||||
[json| { "a":null, "b":"foo" } |]
|
[json| { "a":null, "b":"foo" } |]
|
||||||
liftIO $ do
|
`shouldRespondWith`
|
||||||
simpleBody p `shouldBe` [json| [{ "a":null, "b":"foo" }] |]
|
[json| [{ "a":null, "b":"foo" }] |]
|
||||||
lookup hLocation (simpleHeaders p) `shouldBe` Nothing
|
{ matchStatus = 201
|
||||||
simpleStatus p `shouldBe` created201
|
, matchHeaders = [matchHeaderAbsent hLocation]
|
||||||
|
}
|
||||||
|
|
||||||
context "with compound pk supplied" $
|
context "with compound pk supplied" $
|
||||||
it "builds response location header appropriately" $ do
|
it "builds response location header appropriately" $ do
|
||||||
@@ -210,10 +212,12 @@ spec actualPgVersion = do
|
|||||||
let bulkData = [json| [ {"k1":21, "k2":"hello world"}
|
let bulkData = [json| [ {"k1":21, "k2":"hello world"}
|
||||||
, {"k1":22, "k2":"bye for now"}]
|
, {"k1":22, "k2":"bye for now"}]
|
||||||
|]
|
|]
|
||||||
p <- request methodPost "/compound_pk" [] bulkData
|
request methodPost "/compound_pk" [] bulkData
|
||||||
liftIO $ do
|
`shouldRespondWith`
|
||||||
simpleStatus p `shouldBe` created201
|
""
|
||||||
lookup hLocation (simpleHeaders p) `shouldBe` Nothing
|
{ matchStatus = 201
|
||||||
|
, matchHeaders = [matchHeaderAbsent hLocation]
|
||||||
|
}
|
||||||
|
|
||||||
context "with invalid json payload" $
|
context "with invalid json payload" $
|
||||||
it "fails with 400 and error" $
|
it "fails with 400 and error" $
|
||||||
@@ -416,13 +420,12 @@ spec actualPgVersion = do
|
|||||||
|
|
||||||
context "with unicode values" $
|
context "with unicode values" $
|
||||||
it "succeeds and returns usable location header" $ do
|
it "succeeds and returns usable location header" $ do
|
||||||
let payload = [json| { "k":"圍棋", "extra":"¥" } |]
|
|
||||||
p <- request methodPost "/simple_pk2?select=extra,k"
|
p <- request methodPost "/simple_pk2?select=extra,k"
|
||||||
[("Prefer", "tx=commit"), ("Prefer", "return=representation")]
|
[("Prefer", "tx=commit"), ("Prefer", "return=representation")]
|
||||||
payload
|
[json| { "k":"圍棋", "extra":"¥" } |]
|
||||||
liftIO $ do
|
pure p `shouldRespondWith`
|
||||||
simpleBody p `shouldBe` "["<>payload<>"]"
|
[json|[ { "k":"圍棋", "extra":"¥" } ]|]
|
||||||
simpleStatus p `shouldBe` created201
|
{ matchStatus = 201 }
|
||||||
|
|
||||||
let Just location = lookup hLocation $ simpleHeaders p
|
let Just location = lookup hLocation $ simpleHeaders p
|
||||||
get location
|
get location
|
||||||
@@ -496,12 +499,12 @@ spec actualPgVersion = do
|
|||||||
}
|
}
|
||||||
|
|
||||||
it "can insert in a table with no select and return=minimal" $ do
|
it "can insert in a table with no select and return=minimal" $ do
|
||||||
p <- request methodPost "/insertonly"
|
request methodPost "/insertonly"
|
||||||
[("Prefer", "return=minimal")]
|
[("Prefer", "return=minimal")]
|
||||||
[json| { "v":"some value" } |]
|
[json| { "v":"some value" } |]
|
||||||
liftIO $ do
|
`shouldRespondWith`
|
||||||
simpleBody p `shouldBe` ""
|
""
|
||||||
simpleStatus p `shouldBe` created201
|
{ matchStatus = 201 }
|
||||||
|
|
||||||
describe "Inserting into VIEWs" $
|
describe "Inserting into VIEWs" $
|
||||||
it "returns a location header" $
|
it "returns a location header" $
|
||||||
|
|||||||
+7
-1
@@ -8,7 +8,7 @@ import qualified Data.Set as S
|
|||||||
import qualified System.IO.Error as E
|
import qualified System.IO.Error as E
|
||||||
|
|
||||||
import Data.Aeson (Value (..), decode, encode)
|
import Data.Aeson (Value (..), decode, encode)
|
||||||
import Data.CaseInsensitive (CI (..))
|
import Data.CaseInsensitive (CI (..), original)
|
||||||
import Data.List (lookup)
|
import Data.List (lookup)
|
||||||
import Data.List.NonEmpty (fromList)
|
import Data.List.NonEmpty (fromList)
|
||||||
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
|
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
|
||||||
@@ -33,6 +33,12 @@ matchContentTypeJson = "Content-Type" <:> "application/json; charset=utf-8"
|
|||||||
matchContentTypeSingular :: MatchHeader
|
matchContentTypeSingular :: MatchHeader
|
||||||
matchContentTypeSingular = "Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"
|
matchContentTypeSingular = "Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"
|
||||||
|
|
||||||
|
matchHeaderAbsent :: HeaderName -> MatchHeader
|
||||||
|
matchHeaderAbsent name = MatchHeader $ \headers _body ->
|
||||||
|
case lookup name headers of
|
||||||
|
Just _ -> Just $ "unexpected header: " <> toS (original name) <> "\n"
|
||||||
|
Nothing -> Nothing
|
||||||
|
|
||||||
validateOpenApiResponse :: [Header] -> WaiSession () ()
|
validateOpenApiResponse :: [Header] -> WaiSession () ()
|
||||||
validateOpenApiResponse headers = do
|
validateOpenApiResponse headers = do
|
||||||
r <- request methodGet "/" headers ""
|
r <- request methodGet "/" headers ""
|
||||||
|
|||||||
Reference in New Issue
Block a user