packages feed

hydra-0.13.0: src/test/haskell/Hydra/Json/AesonSpec.hs

-- | Cross-implementation round-trip tests for JSON serialization/parsing.
--   Tests that the new Hydra-native JSON implementation is compatible with
--   the existing Aeson-based implementation.

{-
stack ghci hydra:lib hydra:hydra-test

Test.Hspec.hspec Hydra.Json.AesonSpec.spec
-}

module Hydra.Json.AesonSpec where

import Hydra.Json.Model (Value)
import Hydra.Testing
import Hydra.Parsing (ParseResult(..), ParseSuccess(..), ParseError(..))
import qualified Hydra.Json.Writer as NewSerde
import qualified Hydra.Json.Parser as NewParser
import qualified Hydra.Staging.Json.Serde as AesonSerde
import qualified Hydra.Test.Json.Writer as WriterTests
import qualified Hydra.Test.Json.Parser as ParserTests

import qualified Test.Hspec as H
import qualified Data.List as L


-- | Extract all JSON values from writer test cases (inputs)
extractWriterValues :: TestGroup -> [(String, Value)]
extractWriterValues group = concatMap extractFromCase (testGroupCases group)
    ++ concatMap extractWriterValues (testGroupSubgroups group)
  where
    extractFromCase tc = case testCaseWithMetadataCase tc of
      TestCaseJsonWriter (WriterTestCase input _) ->
        [(testCaseWithMetadataName tc, input)]
      _ -> []

-- | Extract all JSON values from successful parser test cases (expected outputs)
extractParserValues :: TestGroup -> [(String, Value)]
extractParserValues group = concatMap extractFromCase (testGroupCases group)
    ++ concatMap extractParserValues (testGroupSubgroups group)
  where
    extractFromCase tc = case testCaseWithMetadataCase tc of
      TestCaseJsonParser (ParserTestCase _ (ParseResultSuccess success)) ->
        [(testCaseWithMetadataName tc, parseSuccessValue success)]
      _ -> []

-- | All JSON values from both writer and parser test suites
allJsonValues :: [(String, Value)]
allJsonValues = L.nubBy (\(n1, _) (n2, _) -> n1 == n2) $
    extractWriterValues WriterTests.allTests
    ++ extractParserValues ParserTests.allTests

-- | Test: Aeson writer -> New parser -> Compare
aesonWriterNewParserRoundTrip :: H.SpecWith ()
aesonWriterNewParserRoundTrip = H.describe "Aeson writer -> New parser round trip" $ do
  mapM_ makeTest allJsonValues
  where
    makeTest (name, value) = H.it name $ do
      let serialized = AesonSerde.jsonValueToString value
      case NewParser.parseJson serialized of
        ParseResultSuccess success ->
          parseSuccessValue success `H.shouldBe` value
        ParseResultFailure err ->
          H.expectationFailure $ "Parse failed: " ++ parseErrorMessage err
            ++ " at: " ++ take 50 (parseErrorRemainder err)

-- | Test: New writer -> Aeson parser -> Compare
newWriterAesonParserRoundTrip :: H.SpecWith ()
newWriterAesonParserRoundTrip = H.describe "New writer -> Aeson parser round trip" $ do
  mapM_ makeTest allJsonValues
  where
    makeTest (name, value) = H.it name $ do
      let serialized = NewSerde.printJson value
      case AesonSerde.stringToJsonValue serialized of
        Right parsed -> parsed `H.shouldBe` value
        Left err -> H.expectationFailure $ "Parse failed: " ++ err

spec :: H.Spec
spec = do
  aesonWriterNewParserRoundTrip
  newWriterAesonParserRoundTrip