wireform-parquet-0.1.0.0: test-derive/Test/Parquet/Derive.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Test.Parquet.Derive (tests) where
import Data.Vector qualified as V
import Parquet.Derive (
fromParquetRow,
parquetSchemaFor,
toParquetRow,
)
import Parquet.Nested qualified as PN
import Parquet.Types qualified as P
import Test.Parquet.Derive.Instances (sumTypeDeriveSucceeded)
import Test.Parquet.Derive.Types
import Test.Syd
tests :: Spec
tests =
describe "Parquet.Derive" $
sequence_
[ schemaTests
, rowTests
, coercedTests
, spliceTests
]
{- | Spliced once at compile time so the test functions can
pattern-match without re-running TH per case.
-}
saleSchema :: V.Vector P.SchemaElement
saleSchema = $(parquetSchemaFor ''Sale)
orderSchema :: V.Vector P.SchemaElement
orderSchema = $(parquetSchemaFor ''Order)
-- ---------------------------------------------------------------------------
-- Schema
-- ---------------------------------------------------------------------------
schemaTests :: Spec
schemaTests =
describe "schema" $
sequence_
[ it "synthetic root + one leaf per field" $ do
V.length saleSchema `shouldBe` 4
P.seName (V.unsafeIndex saleSchema 0) `shouldBe` "schema"
P.seNumChildren (V.unsafeIndex saleSchema 0) `shouldBe` Just 3
, it "rename modifier reflected on schema element name" $ do
P.seName (V.unsafeIndex saleSchema 1) `shouldBe` "amount"
P.seName (V.unsafeIndex saleSchema 2) `shouldBe` "product"
, it "absent rename falls back to backend's snake_case style" $
P.seName (V.unsafeIndex saleSchema 3) `shouldBe` "sale_region"
, it "amount -> INT64 required" $ do
let se = V.unsafeIndex saleSchema 1
P.seType se `shouldBe` Just P.PTInt64
P.seRepetition se `shouldBe` Just P.Required
, it "product -> BYTE_ARRAY/UTF8 required" $ do
let se = V.unsafeIndex saleSchema 2
P.seType se `shouldBe` Just P.PTByteArray
P.seConvertedType se `shouldBe` Just P.CTUtf8
P.seRepetition se `shouldBe` Just P.Required
, it "Maybe Text region -> BYTE_ARRAY/UTF8 optional" $ do
let se = V.unsafeIndex saleSchema 3
P.seType se `shouldBe` Just P.PTByteArray
P.seConvertedType se `shouldBe` Just P.CTUtf8
P.seRepetition se `shouldBe` Just P.Optional
]
-- ---------------------------------------------------------------------------
-- Per-row codec
-- ---------------------------------------------------------------------------
rowTests :: Spec
rowTests =
describe "row" $
sequence_
[ it "Just region produces three Just leaves" $ do
let s = Sale 100 "widget" (Just "us-east")
row = toParquetRow s
V.length row `shouldBe` 3
V.unsafeIndex row 0 `shouldBe` Just (PN.LvInt64 100)
V.unsafeIndex row 1 `shouldBe` Just (PN.LvString "widget")
V.unsafeIndex row 2 `shouldBe` Just (PN.LvString "us-east")
, it "Nothing region produces a Nothing slot" $ do
let s = Sale 7 "gizmo" Nothing
row = toParquetRow s
V.unsafeIndex row 2 `shouldBe` Nothing
, it "round-trip Sale through toParquetRow/fromParquetRow" $ do
let s = Sale 0xCAFE "wrench" (Just "eu-west")
case fromParquetRow (toParquetRow s) of
Right s' -> s' `shouldBe` s
Left e -> expectationFailure e
, it "round-trip Sale with Nothing region" $ do
let s = Sale 1 "spanner" Nothing
case fromParquetRow (toParquetRow s) of
Right s' -> s' `shouldBe` s
Left e -> expectationFailure e
, it "fromParquetRow rejects wrong leaf count" $
case ( fromParquetRow (V.singleton (Just (PN.LvInt64 1)))
:: Either String Sale
) of
Left _ -> pure ()
Right s -> expectationFailure ("unexpected success: " ++ show s)
, it "fromParquetRow rejects null in required column" $
case ( fromParquetRow
( V.fromList
[ Nothing
, Just (PN.LvString "x")
, Nothing
]
)
:: Either String Sale
) of
Left _ -> pure ()
Right s -> expectationFailure ("unexpected success: " ++ show s)
]
-- ---------------------------------------------------------------------------
-- coerced newtype roundtrip
-- ---------------------------------------------------------------------------
coercedTests :: Spec
coercedTests =
describe "coerced" $
sequence_
[ it "Order schema picks up Int64 from coerced ''Int64" $ do
V.length orderSchema `shouldBe` 2
let leaf = V.unsafeIndex orderSchema 1
P.seType leaf `shouldBe` Just P.PTInt64
P.seRepetition leaf `shouldBe` Just P.Required
, it "Order encodes via the underlying Int64 representation" $ do
let o = Order (OrderId 42)
row = toParquetRow o
V.length row `shouldBe` 1
V.unsafeIndex row 0 `shouldBe` Just (PN.LvInt64 42)
, it "Order round-trips through the coerced leaf" $ do
let o = Order (OrderId 0xDEAD)
case fromParquetRow (toParquetRow o) of
Right o' -> o' `shouldBe` o
Left e -> expectationFailure e
]
-- ---------------------------------------------------------------------------
-- Splice-time refusal of sum types
-- ---------------------------------------------------------------------------
spliceTests :: Spec
spliceTests =
describe "splice-time" $
sequence_
[ it "deriveParquet refuses sum types" $
(not sumTypeDeriveSucceeded) `shouldBe` True
]