packages feed

openapi-hs-4.0.0: test/Data/OpenApi/Schema/RoundtripSpec.hs

{-# LANGUAGE OverloadedStrings #-}

-- | Property-based round-trip coverage for 3.1 'Schema' values (EP-7).
--
-- There is no @Arbitrary Schema@ instance in the tree (EP-3/EP-4 did not add one;
-- see the EP-7 Surprises). Rather than author a full recursive instance for the
-- ~50-field record, this generator combines a random subset of independent
-- single-field "fragments" — each exercising a 3.1 keyword — and asserts the
-- result survives @decode . encode@.
module Data.OpenApi.Schema.RoundtripSpec where

import Data.Aeson (decode, encode)
import Data.OpenApi
import Data.OpenApi.Internal
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck

strSchema :: Schema
strSchema = mempty {_schemaType = Just (OpenApiTypeSingle OpenApiString)}

numSchema :: Schema
numSchema = mempty {_schemaType = Just (OpenApiTypeSingle OpenApiNumber)}

-- | Independent single-field fragments, each setting one 3.1 keyword.
fragments :: [Schema -> Schema]
fragments =
  [ \s -> s {_schemaType = Just (OpenApiTypeArray [OpenApiString, OpenApiNull])},
    \s -> s {_schemaExclusiveMinimum = Just 0, _schemaExclusiveMaximum = Just 100},
    \s -> s {_schemaMaximum = Just 10},
    \s -> s {_schemaConst = Just "fixed"},
    \s -> s {_schemaPrefixItems = Just [Inline strSchema, Inline numSchema]},
    \s -> s {_schemaItems = Just (OpenApiItemsBoolean False)},
    \s -> s {_schemaContains = Just (Inline numSchema), _schemaMinContains = Just 1},
    \s -> s {_schemaIf = Just (Inline strSchema), _schemaThen = Just (Inline numSchema)},
    \s -> s {_schemaExamples = Just ["a", "b"]},
    \s -> s {_schemaId = Just "https://example/s"},
    \s -> s {_schemaRef = Just "#/$defs/A"},
    \s -> s {_schemaTitle = Just "t", _schemaDescription = Just "d"},
    \s -> s {_schemaUnevaluatedItems = Just (Inline strSchema)}
  ]

genSchema :: Gen Schema
genSchema = do
  fs <- sublistOf fragments
  pure (foldr (\f acc -> f acc) mempty fs)

prop_schema31_roundtrip :: Property
prop_schema31_roundtrip = forAll genSchema $ \s ->
  (decode (encode s) :: Maybe Schema) === Just s

spec :: Spec
spec =
  describe "Schema 3.1 round-trip" $
    prop "decode . encode == Just for random 3.1 schemas" prop_schema31_roundtrip