jsonlogic-0.1.0.0: test/Operation/Data/TestMissingSome.hs
module Operation.Data.TestMissingSome (missingSomeUnitTests, missingSomeGeneratorTests) where
import Generator.Data
import Generator.Generic (genGenericJsonNumber)
import Generator.Utils
import Hedgehog (Gen, forAll, property, (===))
import qualified Hedgehog as H (assert, failure)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import JsonLogic.Json
import JsonLogic.Pure.Evaluator
import Test.Tasty
import Test.Tasty.HUnit as U
import Utils
missingSomeUnitTests :: TestTree
missingSomeUnitTests =
testGroup
"missing_some unit tests"
[ testCase "logic {\"missing_some\":[1, []]} data {} => []" $
U.assertEqual
"empty case"
(Right $ jArr [])
(apply [] (jObj [("missing_some", jArr [jNum 0, jArr []])]) (jObj [])),
testCase "logic {\"missing_some\":[1, [\"a\", \"b\", \"c\"]]} data {\"a\":\"apple\"} => [\"b\",\"c\"]" $
U.assertEqual
"first test case on site"
(Right $ jArr [])
(apply [] (jObj [("missing_some", jArr [jNum 1, jArr [jStr "a", jStr "b", jStr "c"]])]) (jObj [("a", jStr "apple")])),
testCase "logic {\"missing_some\":[2, [\"a\", \"b\", \"c\"]]} data {\"a\":\"apple\"} => [\"b\",\"c\"]" $
U.assertEqual
"second test case on site"
(Right $ jArr [jStr "b", jStr "c"])
(apply [] (jObj [("missing_some", jArr [jNum 2, jArr [jStr "a", jStr "b", jStr "c"]])]) (jObj [("a", jStr "apple")])),
testCase
( "logic {\"if\" :[{\"merge\": [{\"missing\":[\"first_name\", \"last_name\"]},{\"missing_some\":[1, [\"cell_phone\", \"home_phone\"] ]}]}, \"We require first name, last name, and one phone number.\",\"OK to proceed\"]}"
++ "data {\"first_name\":\"Bruce\", \"last_name\":\"Wayne\"} => \"We require first name, last name, and one phone number.\""
)
$ U.assertEqual
"third test case on site"
(Right $ jStr "We require first name, last name, and one phone number.")
(apply [] (jObj [("if", jArr [jObj [("merge", jArr [jObj [("missing", jArr [jStr "first_name", jStr "last_name"])], jObj [("missing_some", jArr [jNum 1, jArr [jStr "cell_phone", jStr "home_phone"]])]])], jStr "We require first name, last name, and one phone number.", jStr "OK to proceed"])]) (jObj [("first_name", jStr "Bruce"), ("last_name", jStr "Wayne")]))
]
missingSomeGeneratorTests :: TestTree
missingSomeGeneratorTests =
testGroup
"missing_some generator tests"
[ hTestProperty "missing_some over empty" $
property $ do
-- Missing some over random integer
(jsonNumber, _) <- forAll genGenericJsonNumber
let missingSomeEmpty = jObj [("missing_some", jArr [jsonNumber, jArr []])]
-- Random Json data
dataJsonArray <- forAll $ Gen.sized genSizedRandomJsonArray
dataJsonObject <- forAll $ Gen.sized genSizedRandomJsonObject
-- Indexing empty missing_some over random data returns empty array
Right (jArr []) === apply [] missingSomeEmpty dataJsonArray
Right (jArr []) === apply [] missingSomeEmpty (JsonObject dataJsonObject),
hTestProperty "missing_some when all keys are present" $
property $ do
-- Generate flat array as data
jsonData@(JsonArray js) <- forAll $ increaseSizeBy 1 $ Gen.sized genSizedFlatArray
-- Generate a random list of indexes
indexes <- forAll $ genRandomJsonIndexes (0, length js - 1)
-- Missing a random integer when all are present will result in an empty array
x <- forAll $ Gen.int $ Range.constant 0 30
let rule = jObj [("missing_some", jArr [jNum $ fromIntegral x, jArr indexes])]
Right (jArr []) === apply [] rule jsonData,
hTestProperty "missing_some when no keys are present" $
property $ do
-- Generate a flat array as data
jsonData@(JsonArray js) <- forAll $ increaseSizeBy 1 $ Gen.sized genSizedFlatArray
-- Generates random list of indexes
missingIndexes <- forAll $ genRandomJsonIndexes (length js, length js * 2)
-- Construct rule
nrMissing <- forAll $ Gen.int $ Range.constant 1 30
let rule = jObj [("missing_some", jArr [jNum $ fromIntegral nrMissing, JsonArray missingIndexes])]
-- Always should return entire list of missing items
Right (jArr missingIndexes) === apply [] rule jsonData,
hTestProperty "missing_some when some keys are present" $
property $ do
-- Generate a flat array as data
jsonData@(JsonArray js) <- forAll $ increaseSizeBy 1 $ Gen.sized genSizedFlatArray
-- On average half of the indexes are missing and half of the indexes are present
presentIndexes <- forAll $ genRandomJsonIndexes (0, length js - 1)
missingIndexes <- forAll $ genRandomJsonIndexes (length js, length js * 2)
-- The amount of items required is put in a rule
nrRequired <- forAll $ fromIntegral <$> (Gen.int $ Range.constant 0 (length js * 2) :: Gen Int)
let rule = jObj [("missing_some", jArr [jNum nrRequired, JsonArray $ presentIndexes ++ missingIndexes])]
-- Evaluate the rule with the data
case apply [] rule jsonData of
-- If there are more indexes present than the number required then it returns an empty array.
Right (JsonArray []) -> H.assert $ fromIntegral (length presentIndexes) >= nrRequired || null missingIndexes
-- Otherwise it returns all the missing items
res@(Right (JsonArray _)) -> Right (jArr missingIndexes) === res
-- Should never return any other type
_ -> H.failure
]
-- | Given a range, generate a random Json list with these indexes as numbers
genRandomJsonIndexes :: (Int, Int) -> Gen [Json]
genRandomJsonIndexes (lowerBound, upperBound) = map (JsonNumber . fromIntegral) <$> Gen.subsequence [lowerBound .. upperBound]