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:
Robert Vollmert
2021-03-14 12:45:02 -05:00
committed by Steve Chavez
parent 5baec4819f
commit 3a466aea9a
2 changed files with 65 additions and 56 deletions
+58 -55
View File
@@ -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
View File
@@ -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 ""