-- | -- -- Module : Network.URI.Template.Test.RFC -- Copyright : (c) 2025 Patrick Brisbin -- License : AGPL-3 -- Maintainer : pbrisbin@gmail.com -- Stability : experimental -- Portability : POSIX module Network.URI.Template.Test.RFC ( runRFCTests ) where import Prelude import Conduit import Control.Monad (void) import Data.Foldable (fold, for_) import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map import Data.Text (Text, unpack) import Data.Text qualified as T import Data.Text.IO qualified as T import Network.URI.Template.Expand import Network.URI.Template.Internal import Network.URI.Template.Internal.Parse (restOfLine) import Network.URI.Template.Internal.Pretty import Network.URI.Template.VarName import Network.URI.Template.VarValue import Test.Hspec import Text.Megaparsec import Text.Megaparsec.Char -- | Parse @rfc6570.txt@ for vars and examples, run them as hspec tests runRFCTests :: HasCallStack => Spec runRFCTests = do t <- runIO $ T.readFile "rfc/examples.txt" case parse (some testCaseP <* eof) "rfc/examples.txt" t of Left err -> do it "parsed examples" $ expectationFailure $ "RFC parse error:\n" <> errorBundlePretty err Right tcs -> do let maxWidth = maximum $ map (length . (.input)) $ concatMap (.examples) tcs padInput s = let n = maxWidth - length s in s <> if n > 0 then replicate n ' ' else "" for_ tcs $ \tc -> do let w = maximum $ map (T.length . unVarName) $ Map.keys tc.vars varDoc = vsep [ "Variables" , indent 6 $ vsep $ map (uncurry $ variablePretty $ max w maxWidth) $ Map.toList tc.vars , indent 4 "Examples" ] context (renderPlain varDoc) $ do for_ tc.examples $ \e -> do it (padInput e.input <> " => " <> unpack e.expected) $ do expandTemplate tc.vars e.template `shouldBe` e.expected data TestCase = TestCase { vars :: Map VarName VarValue , examples :: [TestExample] } data TestExample = TestExample { input :: String , template :: Template , expected :: Text } type Parser = Parsec Void Text testCaseP :: Parser TestCase testCaseP = TestCase . fold <$> someTill (taggedP 'V' variableP) (lookAhead $ void (char 'E') <|> eof) <*> someTill (taggedP 'E' expressnP) (lookAhead $ void (char 'V') <|> eof) taggedP :: Char -> Parser a -> Parser a taggedP c p = char c *> char ' ' *> p <* hspace <* newline expressnP :: Parser TestExample expressnP = do (template, expected) <- (,) <$> templateP <*> (char ' ' *> restOfLine) pure TestExample { input = renderPlain $ templatePretty template , template , expected }