test: add tests for batch upserts (#3005)

This commit is contained in:
Taimoor Zaeem
2023-10-13 09:47:26 -03:00
committed by GitHub
parent dc01c748ae
commit f10b4c3268
2 changed files with 82 additions and 11 deletions
+56
View File
@@ -194,6 +194,62 @@ spec actualPgVersion = do
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" } resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 1.29 totalCost `shouldBe` 1.29
it "outputs the total cost for 2 upserts" $ do
r <- request methodPost "/tiobe_pls"
[("Prefer","handling=strict; resolution=merge-duplicates"), ("Accept","application/vnd.pgrst.plan+json")]
[json| [ { "name": "Python", "rank": 19 }, { "name": "Go", "rank": 20} ]|]
let totalCost = planCost r
resStatus = simpleStatus r
resHeaders = simpleHeaders r
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.27
it "outputs the total cost for an upsert with 10 rows" $ do
r <- request methodPost "/tiobe_pls"
[("Prefer","handling=strict; resolution=merge-duplicates"), ("Accept","application/vnd.pgrst.plan+json")]
(getInsertDataForTiobePlsTable 10)
let totalCost = planCost r
resStatus = simpleStatus r
resHeaders = simpleHeaders r
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.27
it "outputs the total cost for an upsert with 100 rows" $ do
r <- request methodPost "/tiobe_pls"
[("Prefer","handling=strict; resolution=merge-duplicates"), ("Accept","application/vnd.pgrst.plan+json")]
(getInsertDataForTiobePlsTable 100)
let totalCost = planCost r
resStatus = simpleStatus r
resHeaders = simpleHeaders r
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.27
it "outputs the total cost for an upsert with 1000 rows" $ do
r <- request methodPost "/tiobe_pls"
[("Prefer","handling=strict; resolution=merge-duplicates"), ("Accept","application/vnd.pgrst.plan+json")]
(getInsertDataForTiobePlsTable 1000)
let totalCost = planCost r
resStatus = simpleStatus r
resHeaders = simpleHeaders r
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.27
it "outputs the plan for application/vnd.pgrst.object" $ do it "outputs the plan for application/vnd.pgrst.object" $ do
r <- request methodDelete "/projects?id=eq.6" r <- request methodDelete "/projects?id=eq.6"
[("Prefer", "return=representation"), ("Accept", "application/vnd.pgrst.plan+json; for=\"application/vnd.pgrst.object\"; options=verbose")] "" [("Prefer", "return=representation"), ("Accept", "application/vnd.pgrst.plan+json; for=\"application/vnd.pgrst.object\"; options=verbose")] ""
+26 -11
View File
@@ -1,6 +1,7 @@
module SpecHelper where module SpecHelper where
import Control.Lens ((^?)) import Control.Lens ((^?))
import qualified Data.Aeson as JSON
import Data.Aeson.Lens import Data.Aeson.Lens
import qualified Data.ByteString.Base64 as B64 (decodeLenient) import qualified Data.ByteString.Base64 as B64 (decodeLenient)
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
@@ -9,7 +10,7 @@ import qualified Data.Map.Strict as M
import Data.Scientific (toRealFloat) import Data.Scientific (toRealFloat)
import qualified Data.Set as S import qualified Data.Set as S
import Data.Aeson (Value (..), decode, encode) import Data.Aeson ((.=))
import Data.CaseInsensitive (CI (..), mk, original) import Data.CaseInsensitive (CI (..), mk, original)
import Data.List (lookup) import Data.List (lookup)
import Data.List.NonEmpty (fromList) import Data.List.NonEmpty (fromList)
@@ -79,14 +80,14 @@ validateOpenApiResponse headers = do
let respHeaders = simpleHeaders r in let respHeaders = simpleHeaders r in
respHeaders `shouldSatisfy` respHeaders `shouldSatisfy`
\hs -> ("Content-Type", "application/openapi+json; charset=utf-8") `elem` hs \hs -> ("Content-Type", "application/openapi+json; charset=utf-8") `elem` hs
Just body <- pure $ decode (simpleBody r) Just body <- pure $ JSON.decode (simpleBody r)
Just schema <- liftIO $ decode <$> BL.readFile "test/spec/fixtures/openapi.json" Just schema <- liftIO $ JSON.decode <$> BL.readFile "test/spec/fixtures/openapi.json"
let args :: M.Map Text Value let args :: M.Map Text JSON.Value
args = M.fromList args = M.fromList
[ ( "schema", schema ) [ ( "schema", schema )
, ( "data", body ) ] , ( "data", body ) ]
hdrs = acceptHdrs "application/json" hdrs = acceptHdrs "application/json"
request methodPost "/rpc/validate_json_schema" hdrs (encode args) request methodPost "/rpc/validate_json_schema" hdrs (JSON.encode args)
`shouldRespondWith` "true" `shouldRespondWith` "true"
{ matchStatus = 200 { matchStatus = 200
, matchHeaders = [] , matchHeaders = []
@@ -284,7 +285,7 @@ isErrorFormat s =
"message" `S.member` keys && "message" `S.member` keys &&
S.null (S.difference keys validKeys) S.null (S.difference keys validKeys)
where where
obj = decode s :: Maybe (M.Map Text Value) obj = JSON.decode s :: Maybe (M.Map Text JSON.Value)
keys = maybe S.empty M.keysSet obj keys = maybe S.empty M.keysSet obj
validKeys = S.fromList ["message", "details", "hint", "code"] validKeys = S.fromList ["message", "details", "hint", "code"]
@@ -309,7 +310,7 @@ mutatesWith = MutationCheck
-- | The original table data before it is modified. -- | The original table data before it is modified.
-- The column order is needed for an accurate comparison after the mutation -- The column order is needed for an accurate comparison after the mutation
baseTable :: ByteString -> ByteString -> Value -> BaseTable baseTable :: ByteString -> ByteString -> JSON.Value -> BaseTable
baseTable = BaseTable baseTable = BaseTable
-- | The mutation (update/delete) that will be applied to the base table -- | The mutation (update/delete) that will be applied to the base table
@@ -317,7 +318,7 @@ requestMutation :: Method -> ByteString -> [Header] -> BL.ByteString -> WaiExpec
requestMutation method path headers body = requestMutation method path headers body =
request method path (("Prefer", "tx=commit") : headers) body `shouldRespondWith` 204 request method path (("Prefer", "tx=commit") : headers) body `shouldRespondWith` 204
data BaseTable = BaseTable ByteString ByteString Value data BaseTable = BaseTable ByteString ByteString JSON.Value
data MutationCheck = MutationCheck BaseTable (WaiExpectation ()) data MutationCheck = MutationCheck BaseTable (WaiExpectation ())
planCost :: SResponse -> Float planCost :: SResponse -> Float
@@ -326,6 +327,20 @@ planCost resp =
-- big value in case parsing fails -- big value in case parsing fails
fromMaybe 1000000000.0 $ unbox =<< res fromMaybe 1000000000.0 $ unbox =<< res
where where
unbox :: Value -> Maybe Float unbox :: JSON.Value -> Maybe Float
unbox (Number n) = Just $ toRealFloat n unbox (JSON.Number n) = Just $ toRealFloat n
unbox _ = Nothing unbox _ = Nothing
data TiobePlsRow = TiobePlsRow {
name' :: Text,
rank :: Int
} deriving (Show)
instance JSON.ToJSON TiobePlsRow where
toJSON (TiobePlsRow name'' rank') = JSON.object ["name" .= name'', "rank" .= rank']
getInsertDataForTiobePlsTable :: Int -> BL.ByteString
getInsertDataForTiobePlsTable rows =
JSON.encode $ fromList $ [TiobePlsRow {name' = nm, rank = rk} | (nm,rk) <- nameRankList]
where
nameRankList = [("Lang " <> show i, i) | i <- [20..(rows+20)] ] :: [(Text, Int)]