Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Chore] Replace firefly with scotty in tests #305

Merged
merged 1 commit into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,6 @@ jobs:
path: ~/AppData/Local/Programs/stack
key: ${{ runner.os }}-${{ matrix.ghc }}-appdata-stack


# When editing this action, make sure it can run without using cached folders.
# Yes, it tries to install mingw-w64-x86_64-pcre twice
- name: install pacman dependencies
run: |
stack --system-ghc exec -- pacman -S --needed --noconfirm pkgconf;
stack --system-ghc exec -- pacman -S --needed --noconfirm msys2-keyring;
stack --system-ghc exec -- pacman --noconfirm -Syuu;
stack --system-ghc exec -- pacman -S --needed --noconfirm mingw-w64-x86_64-pcre;
stack --system-ghc exec -- pacman --noconfirm -Syuu;
stack --system-ghc exec -- pacman -S --needed --noconfirm mingw-w64-x86_64-pcre;
stack --system-ghc exec -- pacman -S --needed --noconfirm pcre-devel;

- name: Build
run: |
stack build --system-ghc --stack-yaml ${{ matrix.stackyaml }} --test --bench --no-run-tests --no-run-benchmarks --ghc-options '-Werror'
Expand Down
4 changes: 3 additions & 1 deletion package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ tests:
- cmark-gfm
- containers
- directory
- firefly
- wai
- warp
- scotty
- http-types
- lens
- modern-uri
Expand Down
1 change: 0 additions & 1 deletion stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ packages:
- .

extra-deps:
- firefly-0.2.1.0@sha256:e9d73486464c3e223ec457e02b30ddd5b550fdbf6292b268c64581e2b07d888b,1519
- cmark-gfm-0.2.5
- nyan-interpolation-core-0.9.2
- nyan-interpolation-0.9.2
Expand Down
7 changes: 0 additions & 7 deletions stack.yaml.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@
# https://docs.haskellstack.org/en/stable/lock_files

packages:
- completed:
hackage: firefly-0.2.1.0@sha256:e9d73486464c3e223ec457e02b30ddd5b550fdbf6292b268c64581e2b07d888b,1519
pantry-tree:
sha256: 51d4bf283e1d9ae37e43cd387b112919e45f2fc088f57cbd33c8bad9b0c179f1
size: 600
original:
hackage: firefly-0.2.1.0@sha256:e9d73486464c3e223ec457e02b30ddd5b550fdbf6292b268c64581e2b07d888b,1519
- completed:
hackage: cmark-gfm-0.2.5@sha256:a53b3c6ed20b5476ae18df5f28ababbb6ec8543f9a0758f0381a532d7a879fc0,5188
pantry-tree:
Expand Down
58 changes: 37 additions & 21 deletions tests/Test/Xrefcheck/RedirectChainSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import Universum hiding ((.~))

import Control.Lens ((.~))
import Data.CaseInsensitive qualified as CI
import Data.Map qualified as M
import Network.HTTP.Types (mkStatus)
import Network.HTTP.Types.Header (hLocation)
import Network.HTTP.Types (movedPermanently301)
import Network.HTTP.Types.Header (HeaderName, hLocation)
import Network.Wai.Handler.Warp qualified as Web
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase)
import Web.Firefly (App, ToResponse (toResponse), route, run)
import Web.Scotty qualified as Web

import Test.Xrefcheck.UtilRequests
import Xrefcheck.Config
Expand Down Expand Up @@ -115,32 +115,48 @@ test_redirectRequests = testGroup "Redirect chain tests"
& cNetworkingL . ncExternalRefRedirectsL .~ [RedirectRule Nothing Nothing Nothing RROFollow]
& cNetworkingL . ncMaxRedirectFollowsL .~ limit

redirectRoute :: Text -> Maybe Text -> App ()
redirectRoute name to = route name $ pure $ toResponse
( "" :: Text
, mkStatus 301 "Permanent redirect"
, M.fromList [(CI.map (decodeUtf8 @Text) hLocation, maybeToList to)]
)
setHeader :: HeaderName -> Text -> Web.ActionM ()
setHeader hdr value = Web.setHeader (decodeUtf8 (CI.original hdr)) (fromStrict value)

mockRedirect :: IO ()
mockRedirect = do
run 5000 do
Web.run 5000 <=< Web.scottyApp $ do
-- A set of redirect routes that correspond to a broken chain.
redirectRoute "/broken1" $ Just $ link "/broken2"
redirectRoute "/broken2" $ Just $ link "/broken3"
redirectRoute "/broken3" Nothing
Web.matchAny "/broken1" $ do
setHeader hLocation (link "/broken2")
Web.status movedPermanently301
Web.matchAny "/broken2" $ do
setHeader hLocation (link "/broken3")
Web.status movedPermanently301
Web.matchAny "/broken3" $ do
-- hLocation: no value
Web.status movedPermanently301

-- A set of redirect routes that correspond to a cycle.
redirectRoute "/cycle1" $ Just $ link "/cycle2"
redirectRoute "/cycle2" $ Just $ link "/cycle3"
redirectRoute "/cycle3" $ Just $ link "/cycle4"
redirectRoute "/cycle4" $ Just $ link "/cycle2"
Web.matchAny "/cycle1" $ do
setHeader hLocation (link "/cycle2")
Web.status movedPermanently301
Web.matchAny "/cycle2" $ do
setHeader hLocation (link "/cycle3")
Web.status movedPermanently301
Web.matchAny "/cycle3" $ do
setHeader hLocation (link "/cycle4")
Web.status movedPermanently301
Web.matchAny "/cycle4" $ do
setHeader hLocation (link "/cycle2")
Web.status movedPermanently301

-- Relative redirects.
redirectRoute "/relative/host" $ Just "/cycle2"
redirectRoute "/relative/path" $ Just "host"
Web.matchAny "/relative/host" $ do
setHeader hLocation "/cycle2"
Web.status movedPermanently301
Web.matchAny "/relative/path" $ do
setHeader hLocation "host"
Web.status movedPermanently301

-- To other host
otherMockRedirect :: IO ()
otherMockRedirect =
run 5001 $ redirectRoute "/other/host" $ Just $ link "/relative/host"
Web.run 5001 <=< Web.scottyApp $ Web.matchAny "/other/host" $ do
setHeader hLocation (link "/relative/host")
Web.status movedPermanently301
43 changes: 23 additions & 20 deletions tests/Test/Xrefcheck/RedirectConfigSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import Universum hiding ((%~), (.~))

import Control.Lens ((%~), (.~))
import Data.CaseInsensitive qualified as CI
import Data.Map qualified as M
import Network.HTTP.Types (mkStatus)
import Network.HTTP.Types.Header (hLocation)
import Network.HTTP.Types (found302, movedPermanently301, temporaryRedirect307)
import Network.HTTP.Types.Header (HeaderName, hLocation)
import Network.Wai.Handler.Warp qualified as Web
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase)
import Text.Regex.TDFA.Text qualified as R
import Web.Firefly (App, Status, ToResponse (toResponse), route, run)
import Web.Scotty qualified as Web

import Test.Xrefcheck.UtilRequests
import Xrefcheck.Config
Expand Down Expand Up @@ -156,20 +156,13 @@ test_redirectRequests = testGroup "Redirect config tests"
regex :: Text -> Maybe R.Regex
regex = rightToMaybe . R.compile defaultCompOption defaultExecOption

status :: Int -> Status
status code = mkStatus code "Redirect"

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

redirectRoute :: Text -> Int -> Maybe Text -> App ()
redirectRoute name code to = route name $ pure $ toResponse
( "" :: Text
, status code
, M.fromList [(CI.map (decodeUtf8 @Text) hLocation, fmap link $ maybeToList to)]
)
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
Expand All @@ -181,10 +174,20 @@ test_redirectRequests = testGroup "Redirect config tests"

mockRedirect :: IO ()
mockRedirect =
run 5000 do
route "/ok" $ pure $ toResponse ("Ok" :: Text)
redirectRoute "/permanent-redirect" 301 $ Just "/ok"
redirectRoute "/temporary-redirect" 302 $ Just "/ok"
redirectRoute "/follow1" 301 $ Just "/follow2"
redirectRoute "/follow2" 302 $ Just "/follow3"
redirectRoute "/follow3" 307 $ Just "/ok"
Web.run 5000 <=< 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
18 changes: 10 additions & 8 deletions tests/Test/Xrefcheck/RedirectDefaultSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ module Test.Xrefcheck.RedirectDefaultSpec where
import Universum

import Data.CaseInsensitive qualified as CI
import Data.Map qualified as M
import Data.Set qualified as S
import Network.HTTP.Types (Status, mkStatus)
import Network.HTTP.Types.Header (hLocation)
import Network.HTTP.Types.Header (HeaderName, hLocation)
import Network.Wai.Handler.Warp qualified as Web
import Test.Tasty (TestName, TestTree, testGroup)
import Test.Tasty.HUnit (Assertion, testCase)
import Web.Firefly (ToResponse (toResponse), route, run)
import Web.Scotty qualified as Web

import Test.Xrefcheck.UtilRequests
import Xrefcheck.Config
Expand Down Expand Up @@ -78,8 +78,10 @@ test_redirectRequests = testGroup "Redirect response defaults"

mockRedirect :: Maybe Text -> Status -> IO ()
mockRedirect expectedLocation expectedStatus =
run 5000 $ route "/redirect" $ pure $ toResponse
( "" :: Text
, expectedStatus
, M.fromList [(CI.map (decodeUtf8 @Text) hLocation, maybeToList expectedLocation)]
)
Web.run 5000 <=< Web.scottyApp $
Web.matchAny "/redirect" $ do
whenJust expectedLocation (setHeader hLocation)
Web.status expectedStatus

setHeader :: HeaderName -> Text -> Web.ActionM ()
setHeader hdr value = Web.setHeader (decodeUtf8 (CI.original hdr)) (fromStrict value)
29 changes: 15 additions & 14 deletions tests/Test/Xrefcheck/TimeoutSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import Universum hiding ((.~))

import Control.Lens ((.~))
import Data.CaseInsensitive qualified as CI
import Data.Map qualified as M
import Data.Set qualified as S
import Network.HTTP.Types (ok200, tooManyRequests429)
import Network.HTTP.Types.Header (hRetryAfter)
import Network.HTTP.Types.Header (HeaderName, hRetryAfter)
import Network.Wai.Handler.Warp qualified as Web
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase)
import Time (Second, Time, sec, threadDelay)
import Web.Firefly (ToResponse (toResponse), route, run)
import Web.Scotty qualified as Web

import Test.Xrefcheck.UtilRequests
import Xrefcheck.Config
Expand Down Expand Up @@ -122,24 +122,25 @@ test_timeout = testGroup "Timeout tests"
mockTimeout :: Time Second -> [MockTimeoutBehaviour] -> IO ()
mockTimeout timeout behList = do
ref <- newIORef @_ behList
run 5000 $ do
route "/timeout" $ handler ref
route "/timeoutother" $ handler ref
Web.run 5000 <=< Web.scottyApp $ do
Web.matchAny "/timeout" $ handler ref
Web.matchAny "/timeoutother" $ handler ref
where
handler ref = do
mbCurrentAction <- atomicModifyIORef' ref $ \case
b : bs -> (bs, Just b)
[] -> ([], Nothing)
let success = toResponse ("" :: Text, ok200, M.empty @(CI.CI Text) @[Text])
case mbCurrentAction of
Nothing -> pure success
Just Ok -> pure success
Nothing -> Web.status ok200
Just Ok -> Web.status ok200
Just Delay -> do
threadDelay timeout
pure $ toResponse ("" :: Text, ok200, M.empty @(CI.CI Text) @[Text])
Just Respond429 ->
pure $ toResponse
("" :: Text, tooManyRequests429,
M.fromList [(CI.map (decodeUtf8 @Text) hRetryAfter, ["1" :: Text])])
Web.status ok200
Just Respond429 -> do
setHeader hRetryAfter "1"
Web.status tooManyRequests429

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

data MockTimeoutBehaviour = Respond429 | Delay | Ok
49 changes: 25 additions & 24 deletions tests/Test/Xrefcheck/TooManyRequestsSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import Universum
import Control.Concurrent (forkIO, killThread)
import Control.Exception qualified as E
import Data.CaseInsensitive qualified as CI
import Data.Map qualified as M
import Data.Set qualified as S
import Data.Time (addUTCTime, defaultTimeLocale, formatTime, getCurrentTime, rfc822DateFormat)
import Data.Time.Clock.POSIX (getPOSIXTime)
import Network.HTTP.Types (Status (..), ok200, serviceUnavailable503, tooManyRequests429)
import Network.HTTP.Types.Header (hRetryAfter)
import Network.HTTP.Types.Header (HeaderName, hRetryAfter)
import Network.Wai (requestMethod)
import Network.Wai.Handler.Warp qualified as Web
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertBool, testCase, (@?=))
import Time (sec, (-:-))
import Web.Firefly (ToResponse (toResponse), getMethod, route, run)
import Web.Scotty qualified as Web

import Test.Xrefcheck.UtilRequests
import Xrefcheck.Core
Expand Down Expand Up @@ -113,9 +114,10 @@ test_tooManyRequests = testGroup "429 response tests"
mock429WithGlobalIORef :: IORef [(Text, Status)] -> IO ()
mock429WithGlobalIORef infoReverseAccumulatorRef = do
callCountRef <- newIORef @_ @Int 0
run 5000 $ do
route "/429grandfinale" $ do
m <- getMethod
Web.run 5000 <=< Web.scottyApp $
Web.matchAny "/429grandfinale" $ do
req <- Web.request
let m = decodeUtf8 (requestMethod req)
callCount <- atomicModifyIORef' callCountRef $ \cc -> (cc + 1, cc)
atomicModifyIORef' infoReverseAccumulatorRef $ \lst ->
( ( m
Expand All @@ -125,14 +127,12 @@ test_tooManyRequests = testGroup "429 response tests"
) : lst
, ()
)
pure $ if
| m == "GET" -> toResponse ("" :: Text, ok200)
| callCount == 0 -> toResponse
( "" :: Text
, tooManyRequests429
, M.fromList [(CI.map (decodeUtf8 @Text) hRetryAfter, ["1" :: Text])]
)
| otherwise -> toResponse ("" :: Text, serviceUnavailable503)
if
| m == "GET" -> Web.status ok200
| callCount == 0 -> do
Web.status tooManyRequests429
setHeader hRetryAfter "1"
| otherwise -> Web.status serviceUnavailable503
infoReverseAccumulatorRef <- newIORef []
setRef <- newIORef S.empty
E.bracket (forkIO $ mock429WithGlobalIORef infoReverseAccumulatorRef) killThread $ \_ -> do
Expand All @@ -150,14 +150,15 @@ test_tooManyRequests = testGroup "429 response tests"
mock429 :: Text -> Status -> IO ()
mock429 retryAfter status = do
callCountRef <- newIORef @_ @Int 0
run 5000 $
route "/429" $ do
Web.run 5000 <=< Web.scottyApp $
Web.matchAny "/429" $ do
callCount <- atomicModifyIORef' callCountRef $ \cc -> (cc + 1, cc)
pure $
if callCount == 0
then toResponse
( "" :: Text
, tooManyRequests429
, M.fromList [(CI.map (decodeUtf8 @Text) hRetryAfter, [retryAfter])]
)
else toResponse ("" :: Text, status)
if callCount == 0
then do
setHeader hRetryAfter retryAfter
Web.status tooManyRequests429
else do
Web.status status

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