packages feed

jsonlogic-0.1.0.0: test/Operation/String/TestSubstr.hs

module Operation.String.TestSubstr where

import Generator.Generic
import Hedgehog as H (assert, failure, forAll, property, (===))
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import JsonLogic.Json (Json (..))
import JsonLogic.Pure.Evaluator (apply)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit as U (assertEqual, testCase)
import Utils

substrUnitTests :: TestTree
substrUnitTests =
  testGroup
    "substr unit tests"
    [ testCase "substr empty" $
        U.assertEqual
          "Empty substr should return an empty string"
          (Right $ jStr "")
          (apply [] (jObj [("substr", jArr [])]) jNull),
      testCase "first example site" $
        U.assertEqual
          "logic {\"substr\": [\"jsonlogic\", 4]} => \"logic\""
          (Right $ jStr "logic")
          (apply [] (jObj [("substr", jArr [jStr "jsonlogic", jNum 4])]) jNull),
      testCase "second example site" $
        U.assertEqual
          "logic {\"substr\": [\"jsonlogic\", -5]} => \"logic\""
          (Right $ jStr "logic")
          (apply [] (jObj [("substr", jArr [jStr "jsonlogic", jNum $ -5])]) jNull),
      testCase "third example site" $
        U.assertEqual
          "logic {\"substr\": [\"jsonlogic\", 1, 3]} => \"son\""
          (Right $ jStr "son")
          (apply [] (jObj [("substr", jArr [jStr "jsonlogic", jNum 1, jNum 3])]) jNull),
      testCase "fourth example site" $
        U.assertEqual
          "logic {\"substr\": [\"jsonlogic\", 4, -2]} => \"son\""
          (Right $ jStr "log")
          (apply [] (jObj [("substr", jArr [jStr "jsonlogic", jNum 4, jNum $ -2])]) jNull),
      testCase "two negative indexes" $
        U.assertEqual
          "logic {\"substr\": [\"jsonlogic\", -5, -2]} => \"log\""
          (Right $ jStr "log")
          (apply [] (jObj [("substr", jArr [jStr "jsonlogic", jNum $ -5, jNum $ -2])]) jNull)
    ]

substrGeneratorTests :: TestTree
substrGeneratorTests =
  testGroup
    "substr generator tests"
    -- Indexing using positive does same as drop
    [ hTestProperty "substr with positive value" $
        property $ do
          (jsonStr, str) <- forAll genGenericJsonString
          index <- forAll $ Gen.int (Range.constant 0 $ length str)
          let rule = jObj [("substr", jArr [jsonStr, jNum $ fromIntegral index])]
          Right (jStr $ drop index str) === apply [] rule jsonStr,
      -- Indexing with negative values returns end
      hTestProperty "substr with negative value" $
        property $ do
          (jsonStr, str) <- forAll genGenericNonEmptyJsonString
          index <- forAll $ Gen.int (Range.constant (-1) (-length str))
          let rule = jObj [("substr", jArr [jsonStr, jNum $ fromIntegral index])]
          case apply [] rule jsonStr of
            -- Length is the equal to the negative index
            Right (JsonString res) -> H.assert $ length res == -index
            _ -> H.failure,
      -- The evaluation returns the same result as take . drop
      hTestProperty "substr with start and final index works like take . drop" $
        property $ do
          (jsonStr, str) <- forAll genGenericJsonString
          -- Take and drop value
          index <- forAll $ Gen.int (Range.constant 0 $ length str)
          endIndex <- forAll $ Gen.int (Range.constant 0 $ length str)
          -- Assert the result is the same
          let rule = jObj [("substr", jArr [jsonStr, jNum $ fromIntegral index, jNum $ fromIntegral endIndex])]
          Right (jStr $ take endIndex $ drop index str) === apply [] rule jsonStr,
      -- The take part also works with negative indexes
      hTestProperty "substr with start and final negative index" $
        property $ do
          (jsonStr, str) <- forAll genGenericNonEmptyJsonString
          -- Positive start index, negative end index
          index <- forAll $ Gen.int (Range.constant 0 $ length str - 1)
          endIndex <- forAll $ Gen.int (Range.constant (-1) (-length str))
          -- Create rule and evaluate
          let rule = jObj [("substr", jArr [jsonStr, jNum $ fromIntegral index, jNum $ fromIntegral endIndex])]
          case apply [] rule jsonStr of
            -- Assert the length is as expected
            Right (JsonString res) -> H.assert $ length res == max 0 (length str - index + endIndex)
            _ -> H.failure
    ]