packages feed

verdict-json-0.0.0.0: test/Verdict/JSONSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-unused-binds #-}
module Verdict.JSONSpec (spec) where

import           Data.Aeson
import           Data.Foldable (toList)
import qualified Data.Map     as Map
import qualified Data.HashMap.Strict as HashMap
import           Data.Proxy
import           Data.Vector  (fromList)
import           GHC.Generics (Generic)
import           Test.Hspec   (Spec, describe, it, shouldBe, shouldContain, context)
import           Verdict

import           Verdict.JSON

spec :: Spec
spec = describe "Verdict.JSON" $ do
    fromJSONSpec
    specSpec


fromJSONSpec :: Spec
fromJSONSpec = describe "FromJSON instance" $ do

  it "does validation when parsing" $ do
    (decode "5" :: Maybe EvenInt) `shouldBe` Nothing

  it "gives a useful error message" $ do
    let Left e = eitherDecode "5" :: Either String EvenInt
    e `shouldContain` "Not a multiple of 2"

  it "parses valid values" $ do
    let (Right expected) = validate 4
    (decode "4" :: Maybe EvenInt) `shouldBe` Just expected

specSpec :: Spec
specSpec = describe "AnySchema" $ do

  context "ToJSON instance" $ do
    let (Object jspec)        = toJSON $ jsonSchema (Proxy :: Proxy Person)
        (Just (Object props)) = HashMap.lookup "properties" jspec
        (Just (Array reqs))   = HashMap.lookup "required" jspec
        (Just (Object ageO))  = HashMap.lookup "age" props
        (Just (Object nameO)) = HashMap.lookup "name" props

    it "lists required properties " $ do
      toList reqs `shouldContain` [String "name"]
      toList reqs `shouldContain` [String "age"]

    it "contains the outermost type" $ do
      HashMap.lookup "type" jspec `shouldBe` Just (String "object")

    it "contains the nested types" $ do
      HashMap.lookup "type" nameO `shouldBe` Just (String "string")
      HashMap.lookup "type" ageO  `shouldBe` Just (String "number")

    it "contains the nested constraints" $ do
      HashMap.lookup "minLength" nameO `shouldBe` Just (Number 1)
      HashMap.lookup "maxLength" nameO `shouldBe` Just (Number 100)
      HashMap.lookup "minimum" ageO `shouldBe` Just (Number 0)
      HashMap.lookup "maximum" ageO `shouldBe` Just (Number 200)



type EvenInt = Validated (MultipleOf 2) Int

type Name  = Validated (MinLength 1 :&& MaxLength 100) String
type Age   = Validated (Minimum 0 :&& Maximum 200) Integer

data Person = Person
    { name :: Name
    , age  :: Age
    } deriving (Eq, Show, Read, Generic, ToJSON)

instance JsonSchema Person where
    type JsonType Person = ObjectSchema
    jsonSchema' _ = mempty { properties = Map.fromList
                                [ ("name", (Required, jsonSchema namep))
                                , ("age" , (Required, jsonSchema agep ))
                                ]
                          }
      where namep = Proxy :: Proxy Name
            agep  = Proxy :: Proxy Age