data-rfc5280-0.1.0.0: test/Rfc5280/Generators.hs
{-# LANGUAGE OverloadedStrings #-}
{- |
Module : Rfc5280.Generators
Copyright : (c) 2026 Tim Emiola
Maintainer : Tim Emiola <adetokunbo@emio.la>
SPDX-License-Identifier: BSD3
Shared QuickCheck generators for X.509 extension test suites.
-}
module Rfc5280.Generators
( validLabel
, validDNSName
, validDnsName
, nameWithInvalidChar
, vectorOf1
)
where
import Data.List.NonEmpty (NonEmpty (..))
import Data.Rfc5280.GeneralName (DnsName, mkDnsName)
import qualified Data.Text as T
import Test.QuickCheck (Gen, choose, elements, vectorOf)
-- | Generates a DNS label of 1–10 lowercase alphanumeric characters.
validLabel :: Gen T.Text
validLabel = do
n <- choose (1, 10)
T.pack <$> vectorOf n (elements (['a' .. 'z'] ++ ['0' .. '9']))
-- | Generates a valid DNS name of 1–4 alphanumeric-only labels.
validDNSName :: Gen T.Text
validDNSName = do
n <- choose (1, 4)
labels <- vectorOf n validLabel
return $ T.intercalate "." labels
{- | Generates a valid 'DnsName'.
Wraps 'validDNSName' and applies 'mkDnsName'; panics if the generator
produces an invalid name (which it never should).
-}
validDnsName :: Gen DnsName
validDnsName = do
t <- validDNSName
case mkDnsName t of
Right dn -> return dn
Left err -> error $ "validDNSName produced invalid DNS name: " <> show err
{- | Like 'vectorOf' but returns a 'NonEmpty' list, guaranteeing at least one
element. The count @n@ must be ≥ 1; callers should enforce this with
'choose' or similar.
-}
vectorOf1 :: Int -> Gen a -> Gen (NonEmpty a)
vectorOf1 n gen = do
h <- gen
tl <- vectorOf (n - 1) gen
return (h :| tl)
-- | Generates a single label with one invalid character injected in the middle.
nameWithInvalidChar :: Gen T.Text
nameWithInvalidChar = do
prefix <- validLabel
badChar <- elements "!@#$%^&*()"
suffix <- validLabel
return $ prefix <> T.singleton badChar <> suffix