wireform-derive-0.1.0.0: test/Test/Derive/NameStyle.hs
{-# LANGUAGE OverloadedStrings #-}
module Test.Derive.NameStyle (tests) where
import qualified Data.Char as Char
import qualified Data.Text as T
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, (@?=))
import Test.Tasty.Hedgehog (testProperty)
import Wireform.Derive.NameStyle
tests :: TestTree
tests = testGroup "NameStyle"
[ testGroup "applyStyle / golden"
[ testCase "snake personName"
(applyStyle SnakeCase "personName" @?= "person_name")
, testCase "snake HTTPRequest"
(applyStyle SnakeCase "HTTPRequest" @?= "http_request")
, testCase "snake httpRequest"
(applyStyle SnakeCase "httpRequest" @?= "http_request")
, testCase "snake-of-snake personName"
(applyStyle SnakeCase "person_name" @?= "person_name")
, testCase "kebab personName"
(applyStyle KebabCase "personName" @?= "person-name")
, testCase "kebab HTTPRequest"
(applyStyle KebabCase "HTTPRequest" @?= "http-request")
, testCase "camel of snake"
(applyStyle CamelCase "person_name" @?= "personName")
, testCase "camel idempotent"
(applyStyle CamelCase "personName" @?= "personName")
, testCase "pascal of snake"
(applyStyle PascalCase "person_name" @?= "PersonName")
, testCase "pascal idempotent"
(applyStyle PascalCase "PersonName" @?= "PersonName")
, testCase "upper snake"
(applyStyle UpperSnake "personName" @?= "PERSON_NAME")
, testCase "upper kebab"
(applyStyle UpperKebab "personName" @?= "PERSON-NAME")
, testCase "strip prefix present"
(applyStyle (StripPrefix "person") "personName" @?= "Name")
, testCase "strip prefix absent"
(applyStyle (StripPrefix "other") "personName" @?= "personName")
, testCase "strip prefix CI"
(applyStyle (StripPrefixCI "PERSON") "personName" @?= "Name")
, testCase "strip suffix present"
(applyStyle (StripSuffix "Name") "personName" @?= "person")
, testCase "compose strip + snake"
(applyStyle
(StripPrefix "person" `andThen` SnakeCase)
"personHttpRequest"
@?= "http_request")
, testCase "replace"
(applyStyle (Replace "Name" "Label") "personName" @?= "personLabel")
, testCase "drop / take"
(applyStyle (DropChars 6 `andThen` TakeChars 2) "personName" @?= "Na")
, testCase "Idiomatic falls through to NoStyle without resolution"
(applyStyle Idiomatic "personName" @?= "personName")
]
, testGroup "Idiomatic resolution"
[ testCase "JSON resolves to CamelCase"
(applyStyle (resolveIdiomatic "json" Idiomatic) "person_name"
@?= "personName")
, testCase "EDN resolves to KebabCase"
(applyStyle (resolveIdiomatic "edn" Idiomatic) "personName"
@?= "person-name")
, testCase "Proto resolves to CamelCase"
(applyStyle (resolveIdiomatic "proto" Idiomatic) "person_name"
@?= "personName")
, testCase "TOML resolves to SnakeCase"
(applyStyle (resolveIdiomatic "toml" Idiomatic) "personName"
@?= "person_name")
, testCase "YAML resolves to KebabCase"
(applyStyle (resolveIdiomatic "yaml" Idiomatic) "personName"
@?= "person-name")
, testCase "XML resolves to PascalCase"
(applyStyle (resolveIdiomatic "xml" Idiomatic) "person_name"
@?= "PersonName")
, testCase "CBOR resolves to NoStyle (verbatim)"
(applyStyle (resolveIdiomatic "cbor" Idiomatic) "personName"
@?= "personName")
, testCase "compose preserves outer style"
(applyStyle (resolveIdiomatic "edn"
(StripPrefix "person" `andThen` Idiomatic))
"personHttpRequest"
@?= "http-request")
]
, testGroup "properties"
[ testProperty "NoStyle is identity" $ property $ do
t <- forAll genIdent
applyStyle NoStyle t === t
, testProperty "Compose NoStyle a == a" $ property $ do
t <- forAll genIdent
applyStyle (Compose NoStyle SnakeCase) t === applyStyle SnakeCase t
, testProperty "snake then camel preserves alpha" $ property $ do
t <- forAll genCamelIdent
let snaked = applyStyle SnakeCase t
roundTripped = applyStyle CamelCase snaked
T.toLower roundTripped === T.toLower t
, testProperty "snake produces lowercase output" $ property $ do
t <- forAll genIdent
let s = applyStyle SnakeCase t
assert (T.all (\c -> not (Char.isUpper c)) s)
, testProperty "kebab produces lowercase output" $ property $ do
t <- forAll genIdent
let s = applyStyle KebabCase t
assert (T.all (\c -> not (Char.isUpper c)) s)
, testProperty "applyStyle is total (never bottom)" $ property $ do
t <- forAll genIdent
let len = T.length (applyStyle SnakeCase t)
assert (len >= 0)
]
]
-- ---------------------------------------------------------------------------
-- Generators
-- ---------------------------------------------------------------------------
-- Identifier-like text: letters, digits, underscores; non-empty.
genIdent :: MonadGen m => m T.Text
genIdent = T.pack <$> Gen.list (Range.linear 1 20)
(Gen.frequency
[ (5, Gen.alpha)
, (1, Gen.element ['_'])
, (1, Gen.digit)
])
-- camelCase identifier: starts lower, no underscores, alphanumeric only.
genCamelIdent :: MonadGen m => m T.Text
genCamelIdent = do
first <- Gen.lower
rest <- Gen.list (Range.linear 0 12) Gen.alphaNum
pure (T.pack (first : rest))