packages feed

dnsbase-1.0.0.0: tests/literals.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Main (main) where

import qualified Data.ByteString.Short as SB
import qualified System.Exit as Sys

import Net.DNSBase.Domain

import LiteralsParser (testParser, mkWire)

----------------------------------------------------------------------
-- Splice values (built at compile time; the @testParser@ runs in
-- the Q monad during these splices, and a build failure here would
-- mean the dnLit / mbLit machinery rejects valid input).
----------------------------------------------------------------------

dnExample :: Domain
dnExample = $$(dnLit testParser "example.org")

mbAt :: Domain
mbAt = $$(mbLit testParser "user@example.org")

mbDot :: Domain
mbDot = $$(mbLit testParser "user.example.org")

mbBare :: Domain
mbBare = $$(mbLit testParser "postmaster")

----------------------------------------------------------------------
-- Test plumbing (plain stdio, matches tests/domain.hs style).
----------------------------------------------------------------------

fatal :: String -> IO ()
fatal msg = do
    putStrLn $ "literals: " ++ msg
    Sys.exitWith (Sys.ExitFailure 1)

eqOrDie :: (Eq a, Show a) => String -> a -> a -> IO ()
eqOrDie name expected actual
    | expected == actual = pure ()
    | otherwise = fatal $ name ++ ": expected " ++ show expected
                               ++ ", got " ++ show actual

leftOrDie :: Show a => String -> Either e a -> IO ()
leftOrDie name r = case r of
    Left _  -> pure ()
    Right x -> fatal $ name ++ ": expected Left, got Right " ++ show x

----------------------------------------------------------------------
-- Tests
----------------------------------------------------------------------

main :: IO ()
main = do
    -- dnLit / mbLit smoke tests.
    eqOrDie "dnLit/example.org"
            (mkWire ["example", "org"])
            (shortBytes dnExample)
    eqOrDie "mbLit/@"
            (mkWire ["user", "example", "org"])
            (shortBytes mbAt)
    eqOrDie "mbLit/."
            (mkWire ["user", "example", "org"])
            (shortBytes mbDot)
    eqOrDie "mbLit/bare"
            (mkWire ["postmaster"])
            (shortBytes mbBare)

    -- decodePresentationMbox positive cases.
    eqOrDie "decodePresentationMbox/@"
            (Right (mkWire ["user", "example", "org"]))
            (shortBytes <$> decodePresentationMbox testParser "user@example.org")
    eqOrDie "decodePresentationMbox/."
            (Right (mkWire ["user", "example", "org"]))
            (shortBytes <$> decodePresentationMbox testParser "user.example.org")
    eqOrDie "decodePresentationMbox/bare"
            (Right (mkWire ["postmaster"]))
            (shortBytes <$> decodePresentationMbox testParser "postmaster")
    eqOrDie "decodePresentationMbox/bare-trailing-dot"
            (Right (mkWire ["postmaster"]))
            (shortBytes <$> decodePresentationMbox testParser "postmaster.")
    eqOrDie "decodePresentationMbox/bare-trailing-at"
            (Right (mkWire ["postmaster"]))
            (shortBytes <$> decodePresentationMbox testParser "postmaster@")
    eqOrDie "decodePresentationMbox/DDD-escape"
            (Right (mkWire ["A.B", "org"]))
            (shortBytes <$> decodePresentationMbox testParser "A\\046B.org")
    eqOrDie "decodePresentationMbox/C-escape-dot"
            (Right (mkWire ["a.b", "org"]))
            (shortBytes <$> decodePresentationMbox testParser "a\\.b.org")
    eqOrDie "decodePresentationMbox/C-escape-at"
            (Right (mkWire ["a@b", "org"]))
            (shortBytes <$> decodePresentationMbox testParser "a\\@b.org")
    eqOrDie "decodePresentationMbox/escaped-@-then-real-@"
            (Right (mkWire ["u@v", "example", "org"]))
            (shortBytes <$> decodePresentationMbox testParser "u\\@v@example.org")
    -- EAI: literal non-ASCII Chars are UTF-8 encoded into the
    -- localpart wire bytes.  "виктор" -> UTF-8 0xD0 B2 D0 B8 D0
    -- BA D1 82 D0 BE D1 80 (12 bytes).  The IsString instance for
    -- ShortByteString does a byte-per-Char Latin-1 truncation, so
    -- the expected-bytes literal below is the literal wire form.
    eqOrDie "decodePresentationMbox/utf8-cyrillic-localpart"
            (Right (mkWire [ "\xD0\xB2\xD0\xB8\xD0\xBA\xD1\x82\xD0\xBE\xD1\x80"
                           , "example", "org" ]))
            (shortBytes <$> decodePresentationMbox testParser "\1074\1080\1082\1090\1086\1088@example.org")

    -- decodePresentationMbox negative cases.
    leftOrDie "decodePresentationMbox/empty-localpart"
              (decodePresentationMbox testParser "@example.org")
    leftOrDie "decodePresentationMbox/bad-escape-truncated"
              (decodePresentationMbox testParser "u\\")
    -- EAI: \DDD with DDD >= 128 is rejected (no raw high octets
    -- in a pure-ASCII-or-UTF-8 localpart).
    leftOrDie "decodePresentationMbox/high-DDD-escape"
              (decodePresentationMbox testParser "u\\200v@example.org")
    -- EAI: \C with non-ASCII C is rejected for the same reason.
    leftOrDie "decodePresentationMbox/high-C-escape"
              (decodePresentationMbox testParser "u\\\233v@example.org")
    leftOrDie "decodePresentationMbox/unknown-domain"
              (decodePresentationMbox testParser "user@bogus\\")  -- malformed escape rejects in Parse8

    -- wireToDomain positive cases.
    eqOrDie "wireToDomain/root"
            (Just (SB.singleton 0))
            (shortBytes <$> wireToDomain (SB.singleton 0))
    eqOrDie "wireToDomain/single"
            (Just (mkWire ["org"]))
            (shortBytes <$> wireToDomain (mkWire ["org"]))
    eqOrDie "wireToDomain/multi"
            (Just (mkWire ["example", "org"]))
            (shortBytes <$> wireToDomain (mkWire ["example", "org"]))

    -- wireToDomain negative cases (raw byte sequences via SB.pack
    -- so the inputs are unambiguous regardless of OverloadedStrings
    -- decoding).
    eqOrDie "wireToDomain/empty"
            Nothing
            (shortBytes <$> wireToDomain SB.empty)
    eqOrDie "wireToDomain/no-terminator"
            Nothing
            (shortBytes <$> wireToDomain (SB.pack [3, 0x6f, 0x72, 0x67]))
    eqOrDie "wireToDomain/extra-bytes-after-root"
            Nothing
            (shortBytes <$> wireToDomain (SB.pack [3, 0x6f, 0x72, 0x67, 0, 0]))
    eqOrDie "wireToDomain/lying-label-length"
            Nothing
            (shortBytes <$> wireToDomain (SB.pack [16, 0x6f, 0x6f, 0x70, 0x73, 0]))

    putStrLn "literals: all tests passed"