packages feed

xrefcheck-0.3.0: tests/Test/Xrefcheck/RedirectConfigSpec.hs

{- SPDX-FileCopyrightText: 2022 Serokell <https://serokell.io>
 -
 - SPDX-License-Identifier: MPL-2.0
 -}

module Test.Xrefcheck.RedirectConfigSpec where

import Universum hiding ((%~), (.~))

import Control.Lens ((%~), (.~))
import Data.CaseInsensitive qualified as CI
import Network.HTTP.Types (found302, movedPermanently301, temporaryRedirect307)
import Network.HTTP.Types.Header (HeaderName, hLocation)
import Network.Wai qualified as Web
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase)
import Text.Regex.TDFA.Text qualified as R
import Web.Scotty qualified as Web

import Test.Xrefcheck.UtilRequests
import Xrefcheck.Config
import Xrefcheck.Progress
import Xrefcheck.Scan
import Xrefcheck.Verify

test_redirectRequests :: TestTree
test_redirectRequests = testGroup "Redirect config tests"
  [ testGroup "Match"
      [ testGroup "By \"on\""
          [ testCase "Do match" $ do
              setRef <- newIORef mempty
              checkLinkAndProgressWithServer
                (configMod [RedirectRule Nothing Nothing (Just RROTemporary) RROInvalid] [])
                setRef
                (5000, mockRedirect)
                (link "/temporary-redirect")
                (progress False)
                (VerifyResult [RedirectRuleError (chain ["/temporary-redirect", "/ok"]) (Just RROTemporary)])
          , testCase "Do not match" $ do
              setRef <- newIORef mempty
              checkLinkAndProgressWithServer
                (configMod [RedirectRule Nothing Nothing (Just RROPermanent) RROInvalid] [])
                setRef
                (5000, mockRedirect)
                (link "/temporary-redirect")
                (progress True)
                (VerifyResult [])
          ]
      , testGroup "By \"to\""
          [ testCase "Do match" $ do
              setRef <- newIORef mempty
              checkLinkAndProgressWithServer
                (configMod [RedirectRule Nothing (regex ".*/ok") Nothing RROValid] [])
                setRef
                (5000, mockRedirect)
                (link "/permanent-redirect")
                (progress True)
                (VerifyResult [])
          , testCase "Do not match" $ do
              setRef <- newIORef mempty
              checkLinkAndProgressWithServer
                (configMod [RedirectRule Nothing (regex ".*/no-ok") (Just RROPermanent) RROValid] [])
                setRef
                (5000, mockRedirect)
                (link "/permanent-redirect")
                (progress False)
                (VerifyResult [RedirectRuleError (chain ["/permanent-redirect", "/ok"]) (Just RROPermanent)])
          ]
      , testGroup "By \"from\""
          [ testCase "Do match" $ do
              setRef <- newIORef mempty
              checkLinkAndProgressWithServer
                (configMod [RedirectRule (regex ".*/permanent-.*") Nothing Nothing RROValid] [])
                setRef
                (5000, mockRedirect)
                (link "/permanent-redirect")
                (progress True)
                (VerifyResult [])
          , testCase "Do not match" $ do
              setRef <- newIORef mempty
              checkLinkAndProgressWithServer
                (configMod [RedirectRule (regex ".*/temporary-.*") Nothing (Just RROPermanent) RROValid] [])
                setRef
                (5000, mockRedirect)
                (link "/permanent-redirect")
                (progress False)
                (VerifyResult [RedirectRuleError (chain ["/permanent-redirect", "/ok"]) (Just RROPermanent)])
          ]
      , testGroup "By \"from\", \"to\" and \"on\""
        [ testCase "Do match" $ do
            setRef <- newIORef mempty
            checkLinkAndProgressWithServer
              (configMod [RedirectRule (regex ".*/follow[0-9]") (regex "^.*/ok$") (Just (RROCode 307)) RROInvalid] [])
              setRef
              (5000, mockRedirect)
              (link "/follow3")
              (progress False)
              (VerifyResult [RedirectRuleError (chain ["/follow3", "/ok"]) (Just (RROCode 307))])
        , testCase "Do not match" $ do
            setRef <- newIORef mempty
            checkLinkAndProgressWithServer
              (configMod [RedirectRule (regex ".*/follow[0-9]") (regex "^.*/ok$") (Just (RROCode 307)) RROInvalid] [])
              setRef
              (5000, mockRedirect)
              (link "/follow2")
              (progress True)
              (VerifyResult [])
        ]
      , testCase "By any" $ do
          setRef <- newIORef mempty
          checkLinkAndProgressWithServer
            (configMod [RedirectRule Nothing Nothing Nothing RROValid] [])
            setRef
            (5000, mockRedirect)
            (link "/follow1")
            (progress True)
            (VerifyResult [])
      ]
  , testGroup "Chain"
      [ testCase "End valid" $ do
          setRef <- newIORef mempty
          checkLinkAndProgressWithServer
            (configMod [RedirectRule Nothing Nothing Nothing RROFollow] [])
            setRef
            (5000, mockRedirect)
            (link "/follow1")
            (progress True)
            (VerifyResult [])
      , testCase "End invalid" $ do
          setRef <- newIORef mempty
          checkLinkAndProgressWithServer
            (configMod [RedirectRule Nothing Nothing (Just (RROCode 307)) RROInvalid, RedirectRule Nothing Nothing Nothing RROFollow] [])
            setRef
            (5000, mockRedirect)
            (link "/follow1")
            (progress False)
            (VerifyResult [RedirectRuleError (chain ["/follow1", "/follow2", "/follow3", "/ok"]) (Just (RROCode 307))])
      , testCase "Mixed with ignore" $ do
          setRef <- newIORef mempty
          checkLinkAndProgressWithServer
            (configMod [RedirectRule Nothing Nothing (Just (RROCode 307)) RROInvalid, RedirectRule Nothing Nothing Nothing RROFollow] (maybeToList (regex ".*/follow3")))
            setRef
            (5000, mockRedirect)
            (link "/follow1")
            (progress True)
            (VerifyResult [])
      ]
  ]
  where
    link :: Text -> Text
    link = ("http://127.0.0.1:5000" <>)

    chain :: [Text] -> RedirectChain
    chain = fromList . fmap link

    regex :: Text -> Maybe R.Regex
    regex = rightToMaybe . R.compile defaultCompOption defaultExecOption

    configMod :: [RedirectRule] -> [R.Regex] -> Config -> Config
    configMod rules exclussions config = config
      & cNetworkingL . ncExternalRefRedirectsL %~ (rules <>)
      & cExclusionsL . ecIgnoreExternalRefsToL .~ exclussions

    setHeader :: HeaderName -> Text -> Web.ActionM ()
    setHeader hdr value = Web.setHeader (decodeUtf8 (CI.original hdr)) (fromStrict value)

    progress :: Bool -> Progress Int Text
    progress shouldSucceed = report "" $ initProgress 1
      where
        report =
          if shouldSucceed
          then reportSuccess
          else reportError

    mockRedirect :: IO Web.Application
    mockRedirect =
      Web.scottyApp $ do
        Web.matchAny "/ok" $ Web.raw "Ok"
        Web.matchAny "/permanent-redirect" $ do
          setHeader hLocation "/ok"
          Web.status movedPermanently301
        Web.matchAny "/temporary-redirect" $ do
          setHeader hLocation "/ok"
          Web.status found302
        Web.matchAny "/follow1" $ do
          setHeader hLocation "/follow2"
          Web.status movedPermanently301
        Web.matchAny "/follow2" $ do
          setHeader hLocation "/follow3"
          Web.status found302
        Web.matchAny "/follow3" $ do
          setHeader hLocation "/ok"
          Web.status temporaryRedirect307