packages feed

packstream-bolt 0.1.0.0 → 0.1.0.1

raw patch · 2 files changed

+235/−2 lines, 2 filesdep +QuickCheckdep +packstream-boltdep +sandwichPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: QuickCheck, packstream-bolt, sandwich

API changes (from Hackage documentation)

- Data.PackStream.Assoc: instance GHC.Internal.Read.Read a => GHC.Internal.Read.Read (Data.PackStream.Assoc.Assoc a)
- Data.PackStream.Assoc: instance GHC.Internal.Show.Show a => GHC.Internal.Show.Show (Data.PackStream.Assoc.Assoc a)
- Data.PackStream.Generic: instance (Data.PackStream.Generic.GPackStream a, Data.PackStream.Generic.GProdPack b) => Data.PackStream.Generic.GPackStream (a GHC.Internal.Generics.:*: b)
- Data.PackStream.Generic: instance (Data.PackStream.Generic.GPackStream a, Data.PackStream.Generic.GProdPack b) => Data.PackStream.Generic.GProdPack (a GHC.Internal.Generics.:*: b)
- Data.PackStream.Generic: instance (Data.PackStream.Generic.GSumPack a, Data.PackStream.Generic.GSumPack b) => Data.PackStream.Generic.GSumPack (a GHC.Internal.Generics.:+: b)
- Data.PackStream.Generic: instance (Data.PackStream.Generic.GSumPack a, Data.PackStream.Generic.GSumPack b, Data.PackStream.Generic.SumSize a, Data.PackStream.Generic.SumSize b) => Data.PackStream.Generic.GPackStream (a GHC.Internal.Generics.:+: b)
- Data.PackStream.Generic: instance (Data.PackStream.Generic.SumSize a, Data.PackStream.Generic.SumSize b) => Data.PackStream.Generic.SumSize (a GHC.Internal.Generics.:+: b)
- Data.PackStream.Generic: instance (GHC.Internal.Generics.Generic a, Data.PackStream.Generic.GPackStream (GHC.Internal.Generics.Rep a)) => Data.PackStream.Ps.PackStream (Data.PackStream.Generic.GenericPackStream a)
- Data.PackStream.Generic: instance Data.PackStream.Generic.GPackStream GHC.Internal.Generics.U1
- Data.PackStream.Generic: instance Data.PackStream.Generic.GPackStream a => Data.PackStream.Generic.GPackStream (GHC.Internal.Generics.M1 t c a)
- Data.PackStream.Generic: instance Data.PackStream.Generic.GPackStream a => Data.PackStream.Generic.GProdPack (GHC.Internal.Generics.M1 t c a)
- Data.PackStream.Generic: instance Data.PackStream.Generic.GPackStream a => Data.PackStream.Generic.GSumPack (GHC.Internal.Generics.C1 c a)
- Data.PackStream.Generic: instance Data.PackStream.Generic.GSumPack (GHC.Internal.Generics.C1 c GHC.Internal.Generics.U1)
- Data.PackStream.Generic: instance Data.PackStream.Generic.SumSize (GHC.Internal.Generics.C1 c a)
- Data.PackStream.Generic: instance Data.PackStream.Ps.PackStream a => Data.PackStream.Generic.GPackStream (GHC.Internal.Generics.K1 i a)
- Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Internal.Int.Int16
- Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Internal.Int.Int32
- Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Internal.Int.Int64
- Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Internal.Int.Int8
- Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Internal.Word.Word16
- Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Internal.Word.Word32
- Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Internal.Word.Word8
- Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Internal.Int.Int16
- Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Internal.Int.Int32
- Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Internal.Int.Int64
- Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Internal.Int.Int8
- Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Internal.Word.Word16
- Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Internal.Word.Word32
- Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Internal.Word.Word8
- Data.PackStream.Integer: instance GHC.Internal.Enum.Bounded Data.PackStream.Integer.PSInteger
- Data.PackStream.Integer: instance GHC.Internal.Enum.Enum Data.PackStream.Integer.PSInteger
- Data.PackStream.Integer: instance GHC.Internal.Generics.Generic Data.PackStream.Integer.PSInteger
- Data.PackStream.Integer: instance GHC.Internal.Num.Num Data.PackStream.Integer.PSInteger
- Data.PackStream.Integer: instance GHC.Internal.Read.Read Data.PackStream.Integer.PSInteger
- Data.PackStream.Integer: instance GHC.Internal.Real.Integral Data.PackStream.Integer.PSInteger
- Data.PackStream.Integer: instance GHC.Internal.Real.Real Data.PackStream.Integer.PSInteger
- Data.PackStream.Integer: instance GHC.Internal.Show.Show Data.PackStream.Integer.PSInteger
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Internal.Base.String
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Internal.Int.Int16
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Internal.Int.Int32
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Internal.Int.Int64
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Internal.Int.Int8
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Internal.Word.Word16
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Internal.Word.Word32
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Internal.Word.Word8
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream a => Data.PackStream.Ps.PackStream (GHC.Internal.Base.NonEmpty a)
- Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream a => Data.PackStream.Ps.PackStream (GHC.Internal.Maybe.Maybe a)
- Data.PackStream.Ps: instance GHC.Internal.Show.Show Data.PackStream.Ps.Ps
- Data.PackStream.Result: instance GHC.Internal.Base.Alternative Data.PackStream.Result.Result
- Data.PackStream.Result: instance GHC.Internal.Base.Applicative Data.PackStream.Result.Result
- Data.PackStream.Result: instance GHC.Internal.Base.Functor Data.PackStream.Result.Result
- Data.PackStream.Result: instance GHC.Internal.Base.Monad Data.PackStream.Result.Result
- Data.PackStream.Result: instance GHC.Internal.Control.Monad.Fail.MonadFail Data.PackStream.Result.Result
- Data.PackStream.Result: instance GHC.Internal.Data.Foldable.Foldable Data.PackStream.Result.Result
- Data.PackStream.Result: instance GHC.Internal.Data.Traversable.Traversable Data.PackStream.Result.Result
- Data.PackStream.Result: instance GHC.Internal.Generics.Generic (Data.PackStream.Result.Result a)
- Data.PackStream.Result: instance GHC.Internal.Show.Show a => GHC.Internal.Show.Show (Data.PackStream.Result.Result a)
- Data.PackStream.Timestamp: instance GHC.Internal.Enum.Bounded Data.PackStream.Timestamp.PSTimestamp
- Data.PackStream.Timestamp: instance GHC.Internal.Read.Read Data.PackStream.Timestamp.PSTimestamp
- Data.PackStream.Timestamp: instance GHC.Internal.Show.Show Data.PackStream.Timestamp.PSTimestamp
+ Data.PackStream.Assoc: instance GHC.Read.Read a => GHC.Read.Read (Data.PackStream.Assoc.Assoc a)
+ Data.PackStream.Assoc: instance GHC.Show.Show a => GHC.Show.Show (Data.PackStream.Assoc.Assoc a)
+ Data.PackStream.Generic: instance (Data.PackStream.Generic.GPackStream a, Data.PackStream.Generic.GProdPack b) => Data.PackStream.Generic.GPackStream (a GHC.Generics.:*: b)
+ Data.PackStream.Generic: instance (Data.PackStream.Generic.GPackStream a, Data.PackStream.Generic.GProdPack b) => Data.PackStream.Generic.GProdPack (a GHC.Generics.:*: b)
+ Data.PackStream.Generic: instance (Data.PackStream.Generic.GSumPack a, Data.PackStream.Generic.GSumPack b) => Data.PackStream.Generic.GSumPack (a GHC.Generics.:+: b)
+ Data.PackStream.Generic: instance (Data.PackStream.Generic.GSumPack a, Data.PackStream.Generic.GSumPack b, Data.PackStream.Generic.SumSize a, Data.PackStream.Generic.SumSize b) => Data.PackStream.Generic.GPackStream (a GHC.Generics.:+: b)
+ Data.PackStream.Generic: instance (Data.PackStream.Generic.SumSize a, Data.PackStream.Generic.SumSize b) => Data.PackStream.Generic.SumSize (a GHC.Generics.:+: b)
+ Data.PackStream.Generic: instance (GHC.Generics.Generic a, Data.PackStream.Generic.GPackStream (GHC.Generics.Rep a)) => Data.PackStream.Ps.PackStream (Data.PackStream.Generic.GenericPackStream a)
+ Data.PackStream.Generic: instance Data.PackStream.Generic.GPackStream GHC.Generics.U1
+ Data.PackStream.Generic: instance Data.PackStream.Generic.GPackStream a => Data.PackStream.Generic.GPackStream (GHC.Generics.M1 t c a)
+ Data.PackStream.Generic: instance Data.PackStream.Generic.GPackStream a => Data.PackStream.Generic.GProdPack (GHC.Generics.M1 t c a)
+ Data.PackStream.Generic: instance Data.PackStream.Generic.GPackStream a => Data.PackStream.Generic.GSumPack (GHC.Generics.C1 c a)
+ Data.PackStream.Generic: instance Data.PackStream.Generic.GSumPack (GHC.Generics.C1 c GHC.Generics.U1)
+ Data.PackStream.Generic: instance Data.PackStream.Generic.SumSize (GHC.Generics.C1 c a)
+ Data.PackStream.Generic: instance Data.PackStream.Ps.PackStream a => Data.PackStream.Generic.GPackStream (GHC.Generics.K1 i a)
+ Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Int.Int16
+ Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Int.Int32
+ Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Int.Int64
+ Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Int.Int8
+ Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Word.Word16
+ Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Word.Word32
+ Data.PackStream.Integer: instance Data.PackStream.Integer.FromPSInteger GHC.Word.Word8
+ Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Int.Int16
+ Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Int.Int32
+ Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Int.Int64
+ Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Int.Int8
+ Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Word.Word16
+ Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Word.Word32
+ Data.PackStream.Integer: instance Data.PackStream.Integer.ToPSInteger GHC.Word.Word8
+ Data.PackStream.Integer: instance GHC.Enum.Bounded Data.PackStream.Integer.PSInteger
+ Data.PackStream.Integer: instance GHC.Enum.Enum Data.PackStream.Integer.PSInteger
+ Data.PackStream.Integer: instance GHC.Generics.Generic Data.PackStream.Integer.PSInteger
+ Data.PackStream.Integer: instance GHC.Num.Num Data.PackStream.Integer.PSInteger
+ Data.PackStream.Integer: instance GHC.Read.Read Data.PackStream.Integer.PSInteger
+ Data.PackStream.Integer: instance GHC.Real.Integral Data.PackStream.Integer.PSInteger
+ Data.PackStream.Integer: instance GHC.Real.Real Data.PackStream.Integer.PSInteger
+ Data.PackStream.Integer: instance GHC.Show.Show Data.PackStream.Integer.PSInteger
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Base.String
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Int.Int16
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Int.Int32
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Int.Int64
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Int.Int8
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Word.Word16
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Word.Word32
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream GHC.Word.Word8
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream a => Data.PackStream.Ps.PackStream (GHC.Base.NonEmpty a)
+ Data.PackStream.Ps: instance Data.PackStream.Ps.PackStream a => Data.PackStream.Ps.PackStream (GHC.Maybe.Maybe a)
+ Data.PackStream.Ps: instance GHC.Show.Show Data.PackStream.Ps.Ps
+ Data.PackStream.Result: instance Control.Monad.Fail.MonadFail Data.PackStream.Result.Result
+ Data.PackStream.Result: instance Data.Foldable.Foldable Data.PackStream.Result.Result
+ Data.PackStream.Result: instance Data.Traversable.Traversable Data.PackStream.Result.Result
+ Data.PackStream.Result: instance GHC.Base.Alternative Data.PackStream.Result.Result
+ Data.PackStream.Result: instance GHC.Base.Applicative Data.PackStream.Result.Result
+ Data.PackStream.Result: instance GHC.Base.Functor Data.PackStream.Result.Result
+ Data.PackStream.Result: instance GHC.Base.Monad Data.PackStream.Result.Result
+ Data.PackStream.Result: instance GHC.Generics.Generic (Data.PackStream.Result.Result a)
+ Data.PackStream.Result: instance GHC.Show.Show a => GHC.Show.Show (Data.PackStream.Result.Result a)
+ Data.PackStream.Timestamp: instance GHC.Enum.Bounded Data.PackStream.Timestamp.PSTimestamp
+ Data.PackStream.Timestamp: instance GHC.Read.Read Data.PackStream.Timestamp.PSTimestamp
+ Data.PackStream.Timestamp: instance GHC.Show.Show Data.PackStream.Timestamp.PSTimestamp

Files

packstream-bolt.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.38.1.+-- This file has been generated from package.yaml by hpack version 0.38.3. -- -- see: https://github.com/sol/hpack  name:           packstream-bolt-version:        0.1.0.0+version:        0.1.0.1 synopsis:       PackStream binary serialization format description:    Implementation of the PackStream binary serialization format,                 used as the wire format by Neo4j's BOLT protocol.@@ -85,6 +85,48 @@     , text >=2.0 && <2.2     , text-show >=3.10 && <3.12     , time >=1.11 && <1.15+    , unordered-containers ==0.2.*+    , vector ==0.13.*+  default-language: GHC2021+  if flag(dev)+    ghc-options: -Werror++test-suite packstream-bolt-test+  type: exitcode-stdio-1.0+  main-is: Main.hs+  other-modules:+      Paths_packstream_bolt+  hs-source-dirs:+      test+  default-extensions:+      DataKinds+      DefaultSignatures+      DeriveAnyClass+      DerivingVia+      DuplicateRecordFields+      FunctionalDependencies+      GADTs+      LambdaCase+      MultiWayIf+      OverloadedRecordDot+      OverloadedStrings+      PartialTypeSignatures+      RecordWildCards+      RoleAnnotations+      TypeFamilies+      UnboxedSums+      UnboxedTuples+      UnliftedDatatypes+      UnliftedNewtypes+      ViewPatterns+  ghc-options: -Weverything -Wno-all-missed-specialisations -Wno-implicit-prelude -Wno-missed-specialisations -Wno-missing-import-lists -Wno-missing-safe-haskell-mode -Wno-name-shadowing -Wno-overlapping-patterns -Wno-prepositive-qualified-module -Wno-safe -Wno-unsafe -threaded -rtsopts -with-rtsopts=-N -Wno-missing-export-lists -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-orphans+  build-depends:+      QuickCheck+    , base >=4.18 && <5+    , bytestring >=0.11 && <0.13+    , packstream-bolt+    , sandwich+    , text >=2.0 && <2.2     , unordered-containers ==0.2.*     , vector ==0.13.*   default-language: GHC2021
+ test/Main.hs view
@@ -0,0 +1,191 @@+module Main where++import           Test.Sandwich++import           Control.Monad.IO.Class           (liftIO)+import           Data.Int                         (Int64)+import qualified Data.ByteString                  as BS+import qualified Data.HashMap.Lazy                as H+import qualified Data.Text                        as T+import qualified Data.Vector                      as V++import           Data.PackStream                  (pack, unpack)+import           Data.PackStream.Ps               (Ps(..))+import           Data.PackStream.Integer          (ToPSInteger(..))+import           Test.QuickCheck                  (Arbitrary(..), Gen, oneof,+                                                   quickCheckResult, isSuccess,+                                                   withMaxSuccess, chooseInt)+++-- = Helpers++-- | Like 'listOf' but keeps the size small to avoid deep nesting.+listOfSmall :: Gen a -> Gen [a]+listOfSmall g = do+  n <- chooseInt (0, 3)+  sequence (replicate n g)++-- | Generate a finite (non-NaN, non-Infinite) Double.+arbitraryFiniteDouble :: Gen Double+arbitraryFiniteDouble = do+  d <- arbitrary+  pure $ if isNaN d || isInfinite d then 0.0 else d++-- | Arbitrary Ps value (no PsStructure — tag namespace is application-defined).+instance Arbitrary Ps where+  arbitrary = oneof+    [ pure PsNull+    , PsBoolean <$> arbitrary+    , PsInteger . toPSInteger <$> (arbitrary :: Gen Int64)+    , PsFloat <$> arbitraryFiniteDouble+    , PsString . T.pack <$> arbitrary+    , PsBytes . BS.pack <$> arbitrary+    , PsList . V.fromList <$> listOfSmall arbitrary+    , PsDictionary . H.fromList <$> listOfSmall ((,) <$> (T.pack <$> arbitrary) <*> arbitrary)+    ]++-- | Assert a QuickCheck result, failing the sandwich test on property failure.+assertQC r+  | isSuccess r = pure ()+  | otherwise   = expectationFailure $ "QuickCheck: " <> show r+++-- = Unit tests: PackStream round-trips++packstreamRoundTripTests :: TopSpec+packstreamRoundTripTests = describe "PackStream round-trips" $ do+  it "round-trips PsNull" $ do+    unpack (pack PsNull) `shouldBe` Right PsNull++  it "round-trips PsBoolean True" $ do+    unpack (pack (PsBoolean True)) `shouldBe` Right (PsBoolean True)++  it "round-trips PsBoolean False" $ do+    unpack (pack (PsBoolean False)) `shouldBe` Right (PsBoolean False)++  it "round-trips PsInteger small" $ do+    unpack (pack (PsInteger 42)) `shouldBe` Right (PsInteger 42)++  it "round-trips PsInteger negative" $ do+    unpack (pack (PsInteger (-100))) `shouldBe` Right (PsInteger (-100))++  it "round-trips PsInteger zero" $ do+    unpack (pack (PsInteger 0)) `shouldBe` Right (PsInteger 0)++  it "round-trips PsFloat" $ do+    unpack (pack (PsFloat 3.14)) `shouldBe` Right (PsFloat 3.14)++  it "round-trips PsString" $ do+    unpack (pack (PsString "hello world")) `shouldBe` Right (PsString "hello world")++  it "round-trips PsString empty" $ do+    unpack (pack (PsString "")) `shouldBe` Right (PsString "")++  it "round-trips PsString unicode" $ do+    unpack (pack (PsString "\x1F600 emoji")) `shouldBe` Right (PsString "\x1F600 emoji")++  it "round-trips PsList empty" $ do+    unpack (pack (PsList V.empty)) `shouldBe` Right (PsList V.empty)++  it "round-trips PsList with values" $ do+    let v = PsList $ V.fromList [PsInteger 1, PsString "two", PsBoolean True]+    unpack (pack v) `shouldBe` Right v++  it "round-trips PsDictionary empty" $ do+    unpack (pack (PsDictionary H.empty)) `shouldBe` Right (PsDictionary H.empty)++  it "round-trips PsDictionary with entries" $ do+    let d = PsDictionary $ H.fromList [("key", PsString "value"), ("num", PsInteger 42)]+    unpack (pack d) `shouldBe` Right d++  it "round-trips nested structures" $ do+    let nested = PsList $ V.fromList+          [ PsDictionary $ H.fromList [("a", PsInteger 1)]+          , PsList $ V.fromList [PsNull, PsBoolean False]+          ]+    unpack (pack nested) `shouldBe` Right nested++  -- Integer range boundary tests+  it "round-trips PsInteger tiny positive (127)" $ do+    unpack (pack (PsInteger 127)) `shouldBe` Right (PsInteger 127)++  it "round-trips PsInteger tiny negative (-16)" $ do+    unpack (pack (PsInteger (-16))) `shouldBe` Right (PsInteger (-16))++  it "round-trips PsInteger INT_8 range (128)" $ do+    unpack (pack (PsInteger 128)) `shouldBe` Right (PsInteger 128)++  it "round-trips PsInteger INT_8 range (-128)" $ do+    unpack (pack (PsInteger (-128))) `shouldBe` Right (PsInteger (-128))++  it "round-trips PsInteger INT_16 range (1000)" $ do+    unpack (pack (PsInteger 1000)) `shouldBe` Right (PsInteger 1000)++  it "round-trips PsInteger INT_32 range (100000)" $ do+    unpack (pack (PsInteger 100000)) `shouldBe` Right (PsInteger 100000)++  it "round-trips PsInteger INT_64 range (maxBound)" $ do+    let big = PsInteger 9223372036854775807+    unpack (pack big) `shouldBe` Right big++  it "round-trips PsInteger INT_64 range (minBound)" $ do+    let small = PsInteger (-9223372036854775808)+    unpack (pack small) `shouldBe` Right small++  -- Bytes tests+  it "round-trips PsBytes empty" $ do+    unpack (pack (PsBytes "")) `shouldBe` Right (PsBytes "")++  it "round-trips PsBytes with data" $ do+    let bs = PsBytes "\x00\x01\x02\xFF"+    unpack (pack bs) `shouldBe` Right bs++  -- Structure tests+  it "round-trips PsStructure empty fields" $ do+    let s = PsStructure 0x01 V.empty+    unpack (pack s) `shouldBe` Right s++  it "round-trips PsStructure with one field" $ do+    let s = PsStructure 0x01 $ V.singleton $ PsDictionary $ H.fromList+              [("user_agent", PsString "bolty/2.0"), ("scheme", PsString "none")]+    unpack (pack s) `shouldBe` Right s++  it "round-trips PsStructure with multiple fields" $ do+    let s = PsStructure 0x4E $ V.fromList+              [ PsInteger 1+              , PsList (V.fromList [PsString "Person"])+              , PsDictionary (H.fromList [("name", PsString "Alice")])+              , PsString "4:abc:1"+              ]+    unpack (pack s) `shouldBe` Right s++  -- Larger collection tests+  it "round-trips PsList with 16 elements (LIST_8)" $ do+    let v = PsList $ V.fromList [PsInteger (fromIntegral i) | i <- [0..15 :: Int]]+    unpack (pack v) `shouldBe` Right v++  it "round-trips Text via PackStream class" $ do+    let txt = "hello" :: T.Text+    unpack (pack txt) `shouldBe` Right (PsString txt)++  it "round-trips Bool via PackStream class" $ do+    unpack (pack True) `shouldBe` Right (PsBoolean True)+++-- = QuickCheck property tests++quickcheckTests :: TopSpec+quickcheckTests = describe "QuickCheck: pack/unpack round-trip" $ do++  it "Ps round-trips through pack/unpack (1000 cases)" $ do+    r <- liftIO $ quickCheckResult $ withMaxSuccess 1000 $ \(ps :: Ps) ->+      unpack (pack ps) == Right ps+    assertQC r+++-- = Main++main :: IO ()+main = runSandwichWithCommandLineArgs defaultOptions $ do+  packstreamRoundTripTests+  quickcheckTests