packages feed

typesafe-hs-0.1.0.0: test/TypeSafe/Test/Arbitrary.hs

{-# OPTIONS_GHC -Wno-orphans #-}

-- | Schema-valid generators, separate from unknown-discriminator generators.
module TypeSafe.Test.Arbitrary (genText, genNumber, genUnknownQuestion, genUnknownAnswer) where

import Data.Aeson
import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap qualified as KM
import Data.List.NonEmpty qualified as NE
import Data.Map.Strict qualified as Map
import Data.Scientific (Scientific, scientific)
import Data.Text (Text)
import Data.Text qualified as Text
import Data.Vector qualified as V
import Test.QuickCheck
import TypeSafe.Wire

genText :: Gen Text
genText = Text.pack <$> resize 15 (listOf (elements (['a' .. 'z'] <> ['0' .. '9'] <> " _-.`?äöü€日本語🙂\"\\\n\t")))

genNumber :: Gen Scientific
genNumber = frequency [(4, scientific <$> choose (-10000, 10000) <*> choose (-5, 5)), (1, elements [scientific 1 400, scientific (-3) (-500)])]

optional :: Gen a -> Gen (Maybe a)
optional g = frequency [(1, pure Nothing), (2, Just <$> g)]

small :: Gen a -> Gen [a]
small g = chooseInt (0, 3) >>= flip vectorOf g

json :: Int -> Gen Value
json depth =
  oneof $
    [String <$> genText, Number <$> genNumber, Bool <$> arbitrary, pure Null]
      <> if depth <= 0 then [] else [Array . V.fromList <$> small (json (depth - 1)), Object <$> obj (depth - 1)]

obj :: Int -> Gen Object
obj depth = KM.fromList <$> small ((,) <$> (Key.fromText <$> genText) <*> json depth)

instance Arbitrary Content where
  arbitrary = oneof [ContentText <$> genText, ContentObject <$> obj 2, ContentArray . V.fromList <$> small (json 2)]

instance Arbitrary ModelName where arbitrary = ModelName <$> genText

instance Arbitrary QuestionName where arbitrary = QuestionName <$> genText

instance Arbitrary NoulCriteria where arbitrary = NoulCriteria <$> optional arbitrary <*> optional arbitrary

instance Arbitrary NoulQuestion where arbitrary = NoulQuestion <$> optional arbitrary <*> optional arbitrary

instance Arbitrary ChoiceQuestion where
  arbitrary = do
    n <- chooseInt (1, 8)
    -- Fixed distinct labels let shuffled declaration order vary independently.
    keys <- shuffle [Text.pack ("label" <> show i) | i <- [1 .. n]]
    ChoiceQuestion <$> optional arbitrary <*> (V.fromList <$> traverse (\k -> (k,) <$> optional arbitrary) keys)

instance Arbitrary ScoreQuestion where
  arbitrary = ScoreQuestion <$> optional arbitrary <*> (NE.fromList <$> (chooseInt (1, 10) >>= flip vectorOf arbitrary))

instance Arbitrary BoxQuestion where arbitrary = BoxQuestion <$> optional arbitrary

instance Arbitrary ImageFormat where arbitrary = elements [minBound .. maxBound]

instance Arbitrary Asset where arbitrary = Asset <$> optional arbitrary <*> optional genText <*> optional genText

instance Arbitrary Question where
  arbitrary = oneof [QuestionNoul <$> arbitrary, QuestionChoice <$> arbitrary, QuestionScore <$> arbitrary, QuestionBox <$> arbitrary]

instance Arbitrary SystemOneRequest where
  arbitrary = do
    n <- chooseInt (1, 4)
    keys <- shuffle [QuestionName (Text.pack ("question" <> show i)) | i <- [1 .. n]]
    qs <- V.fromList <$> traverse (\k -> (k,) <$> arbitrary) keys
    assets <- frequency [(1, pure Map.empty), (2, Map.fromList <$> traverse (\i -> (Text.pack ("asset" <> show i),) <$> arbitrary) [1 :: Int, 2])]
    SystemOneRequest <$> arbitrary <*> arbitrary <*> pure qs <*> pure assets

instance Arbitrary NoulAnswer where arbitrary = NoulAnswer <$> genNumber

instance Arbitrary ChoiceAnswer where arbitrary = ChoiceAnswer <$> genText <*> genNumber <*> (Map.fromList <$> small ((,) <$> genText <*> genNumber))

instance Arbitrary ScoreAnswer where
  arbitrary = ScoreAnswer <$> genNumber <*> genNumber <*> (Map.fromList <$> small ((,) <$> genText <*> arbitrary)) <*> (Map.fromList <$> small ((,) <$> genText <*> genNumber))

instance Arbitrary BoxCoordinates where arbitrary = BoxCoordinates <$> genNumber <*> genNumber <*> genNumber <*> genNumber

instance Arbitrary BoxAnswer where arbitrary = BoxAnswer <$> arbitrary <*> optional genNumber

instance Arbitrary Answer where
  arbitrary = oneof [AnswerNoul <$> arbitrary, AnswerChoice <$> arbitrary, AnswerScore <$> arbitrary, AnswerBox <$> arbitrary]

instance Arbitrary Usage where
  arbitrary = Usage <$> count <*> count
    where
      count = frequency [(1, pure 0), (1, pure maxBound), (4, chooseInt (0, 100000))]

instance Arbitrary SystemOneResponse where
  arbitrary = do
    n <- chooseInt (1, 4)
    answers <- traverse (\i -> (QuestionName (Text.pack ("q" <> show i)),) <$> arbitrary) [1 .. n]
    SystemOneResponse <$> arbitrary <*> pure (Map.fromList answers) <*> arbitrary

instance Arbitrary ModelMetadata where
  arbitrary = ModelMetadata <$> arbitrary <*> genText <*> elements ["2026-09-15", "2026-01-31"]

instance Arbitrary ModelMetadataList where arbitrary = ModelMetadataList . V.fromList <$> small arbitrary

instance Arbitrary LocationSegment where arbitrary = oneof [LocationField <$> genText, LocationIndex <$> arbitrary]

instance Arbitrary ValidationError where
  arbitrary = ValidationError <$> (V.fromList <$> small arbitrary) <*> genText <*> genText <*> optional genText <*> optional (obj 1)

instance Arbitrary HTTPValidationError where arbitrary = HTTPValidationError <$> optional (V.fromList <$> small arbitrary)

instance Arbitrary BadRequestDetail where arbitrary = oneof [BadRequestDetailText <$> genText, BadRequestDetailObject <$> obj 2]

instance Arbitrary HTTPBadRequestError where arbitrary = HTTPBadRequestError <$> arbitrary

genUnknownQuestion :: Gen Question
genUnknownQuestion = QuestionOther <$> elements ["future", "polygon", "rank"] <*> obj 2

genUnknownAnswer :: Gen Answer
genUnknownAnswer = AnswerOther <$> elements ["future", "polygon", "rank"] <*> obj 2