{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} module Minilang.ServerSpec where import Data.Aeson (FromJSON, eitherDecode) import Data.ByteString (isInfixOf, toStrict) import Data.Data (Proxy (..)) import Minilang.API (Score) import Minilang.Server (serveAPI) import Network.Wai (Application) import Test.Hspec (Spec, it) import Test.Hspec.Wai (get, matchBody, shouldRespondWith, with) import Test.Hspec.Wai.Matcher (MatchBody (..)) spec :: Spec spec = with app $ do it "GET / returns 'index.html'" $ do get "/" `shouldRespondWith` 200 { matchBody = MatchBody $ \_ actual -> if "" `isInfixOf` toStrict actual then Nothing else Just "Expected HTML content" } it "GET /api/leaderboard returns empty list of scores" $ do get "/api/leaderboard" `shouldRespondWith` 200 {matchBody = arrayOfLengthOf (Proxy @Score) 2} arrayOfLengthOf :: forall elem proxy. (FromJSON elem) => proxy elem -> Int -> MatchBody arrayOfLengthOf _ len = MatchBody $ \_ actual -> case eitherDecode actual of Right (scores :: [elem]) -> if length scores == len then Nothing else Just $ "Expected array of length " ++ show len ++ ", but got " ++ show (length scores) Left err -> Just $ "Failed to decode JSON: " ++ err app :: IO Application app = pure serveAPI