content: 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'
  