http-core-0.1.0.0: test/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Http.Core
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
-- ===================================================================
-- Test helpers
-- ===================================================================
assert :: String -> Bool -> IO ()
assert label True = putStrLn $ " OK: " ++ label
assert label False = error $ "FAIL: " ++ label
-- ===================================================================
-- 1. Extensions: typed heterogeneous map
-- ===================================================================
newtype UserId = UserId Int deriving (Eq, Show)
newtype UserName = UserName String deriving (Eq, Show)
testExtensions :: IO ()
testExtensions = do
exts <- emptyExtensions
-- Empty lookup returns Nothing
r1 <- lookupExtension @UserId exts
assert "empty lookup returns Nothing" (r1 == Nothing)
-- Insert and lookup
insertExtension (UserId 42) exts
r2 <- lookupExtension @UserId exts
assert "insert then lookup succeeds" (r2 == Just (UserId 42))
-- Different type returns Nothing
r3 <- lookupExtension @UserName exts
assert "wrong type returns Nothing" (r3 == Nothing)
-- Multiple types coexist
insertExtension (UserName "alice") exts
r4 <- lookupExtension @UserId exts
r5 <- lookupExtension @UserName exts
assert "multiple types: UserId still there" (r4 == Just (UserId 42))
assert "multiple types: UserName found" (r5 == Just (UserName "alice"))
-- Overwrite same type
insertExtension (UserId 99) exts
r6 <- lookupExtension @UserId exts
assert "overwrite replaces value" (r6 == Just (UserId 99))
-- hasExtension
h1 <- hasExtension @UserId exts
h2 <- hasExtension @Bool exts
assert "hasExtension: present" h1
assert "hasExtension: absent" (not h2)
-- deleteExtension
deleteExtension @UserId exts
r7 <- lookupExtension @UserId exts
assert "delete removes value" (r7 == Nothing)
-- UserName still there after deleting UserId
r8 <- lookupExtension @UserName exts
assert "delete doesn't affect other types" (r8 == Just (UserName "alice"))
-- ===================================================================
-- 2. Request construction and splitting
-- ===================================================================
testRequest :: IO ()
testRequest = do
req <- defaultRequest
assert "default method is GET" (requestMethod req == methodGet)
assert "default path is empty" (requestPath req == [])
assert "default body is empty" (requestBody req == BS.empty)
-- splitRequest preserves fields
let (parts, body) = splitRequest req
assert "split: method preserved" (rpMethod parts == methodGet)
assert "split: body extracted" (body == BS.empty)
-- Custom request
exts <- emptyExtensions
let req2 = Request
{ requestMethod = methodPost
, requestPathRaw = "/users/42"
, requestPath = ["users", "42"]
, requestQuery = [("format", Just "json")]
, requestHeaders = [("Content-Type", "application/json")]
, requestBody = "{\"name\":\"alice\"}" :: ByteString
, requestExtensions = exts
}
let (parts2, body2) = splitRequest req2
assert "custom: method is POST" (rpMethod parts2 == methodPost)
assert "custom: path segments" (rpPath parts2 == ["users", "42"])
assert "custom: body content" (body2 == "{\"name\":\"alice\"}")
-- ===================================================================
-- 3. Response construction
-- ===================================================================
testResponse :: IO ()
testResponse = do
let r1 = ok [("Content-Type", "text/plain")] "hello"
assert "ok: status 200" (statusCode (responseStatus r1) == 200)
assert "ok: body" (responseBody r1 == "hello")
let r2 = notFound [] "not here"
assert "notFound: status 404" (statusCode (responseStatus r2) == 404)
assert "noContent: status 204" (statusCode (responseStatus noContent) == 204)
assert "noContent: empty body" (responseBody noContent == BS.empty)
let r3 = serverError [] "oops"
assert "serverError: status 500" (statusCode (responseStatus r3) == 500)
let r4 = created [("Location", "/users/1")] "{\"id\":1}"
assert "created: status 201" (statusCode (responseStatus r4) == 201)
assert "methodNotAllowed: status 405" (statusCode (responseStatus methodNotAllowed) == 405)
let r5 = unprocessable [] "validation failed"
assert "unprocessable: status 422" (statusCode (responseStatus r5) == 422)
-- ===================================================================
-- Main
-- ===================================================================
main :: IO ()
main = do
putStrLn "http-core tests:"
putStrLn ""
putStrLn "Extensions:"
testExtensions
putStrLn ""
putStrLn "Request:"
testRequest
putStrLn ""
putStrLn "Response:"
testResponse
putStrLn ""
putStrLn "All http-core tests passed."