packages feed

servant-routes-0.1.0.0: test/Servant/API/Routes/SomeSpec.hs

module Servant.API.Routes.SomeSpec
  ( spec
  , genSome
  , shrinkSome
  )
where

import Servant.API.Routes.Internal.Some as S
import Test.Hspec as H
import Test.Hspec.QuickCheck as H
import Test.QuickCheck as Q

{- hlint ignore "Monoid law, right identity" -}
{- hlint ignore "Monoid law, left identity" -}
{- hlint ignore "Use fold" -}

genSome :: Q.Gen a -> Q.Gen (Some a)
genSome gen =
  Q.frequency
    [ (1, pure S.None)
    , (2, S.One <$> gen)
    , (4, S.Many . unAtLeast2 <$> genAtLeast2 gen)
    ]

shrinkSome :: (a -> [a]) -> Some a -> [Some a]
shrinkSome shr = fmap S.fromList . Q.shrinkList shr . S.toList

instance Q.Arbitrary a => Q.Arbitrary (Some a) where
  arbitrary = genSome arbitrary
  shrink = shrinkSome shrink

newtype AtLeast2 a = AtLeast2 {unAtLeast2 :: [a]}
  deriving (Show, Eq) via [a]

genAtLeast2 :: Q.Gen a -> Q.Gen (AtLeast2 a)
genAtLeast2 gen = do
  a1 <- gen
  a2 <- gen
  as <- Q.listOf gen
  pure . AtLeast2 $ a1 : a2 : as

instance Arbitrary a => Arbitrary (AtLeast2 a) where
  arbitrary = genAtLeast2 arbitrary
  shrink = fmap AtLeast2 . filter ((>= 2) . length) . shrink . unAtLeast2

spec :: Spec
spec = do
  describe "Some a <-> [a]" $ do
    it "None" $ testList @Int []
    prop "oneType" $ \(x :: Int) -> testList [x]
    prop "manyTypes" $ \(AtLeast2 (xs :: [Int])) -> testList xs
  where
    testList :: forall a. (Eq a, Show a) => [a] -> Q.Property
    testList lst =
      let some = S.fromList lst
          lst' = S.toList some
      in  lst === lst'