From b5ee965d1c97315f86bbd3bad065fabfa09828a5 Mon Sep 17 00:00:00 2001 From: "Julian K. Arni" Date: Mon, 25 Apr 2016 12:31:47 +0200 Subject: [PATCH] Documentation improvements --- src/Servant/QuickCheck.hs | 50 ++++++++++++------- src/Servant/QuickCheck/Internal/Predicates.hs | 49 +++++++++++------- src/Servant/QuickCheck/Internal/QuickCheck.hs | 6 +-- 3 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/Servant/QuickCheck.hs b/src/Servant/QuickCheck.hs index 77c7644..07daf64 100644 --- a/src/Servant/QuickCheck.hs +++ b/src/Servant/QuickCheck.hs @@ -6,14 +6,37 @@ -- tested itself need not be implemented with @servant-server@ (or indeed, -- written in Haskell). -- +-- The documentation of the <#useful-preds Useful predicates> sections is +-- meant to serve as a set of helpful pointers for learning more about best +-- practices concerning REST APIs. module Servant.QuickCheck ( - - -- * Test setup helpers - -- | Helpers to setup and teardown @servant@ servers during tests. - withServantServer - + -- * Property testing + serverSatisfies + -- ** Predicates + -- *** #useful-preds# Useful predicates + -- | The predicates below are often useful. Some check RFC compliance; some are + -- best practice, and some are useful to check that APIs follow in-house + -- best-practices. Included in the documentation for each is a list of + -- references to any relevant RFCs and other links, as well as what type of + -- predicate it is (__RFC Compliance__, __Best Practice__, __Optional__). + -- + -- RFCs distinguish between the force of requirements (e.g. __MUST__ vs. + -- __SHOULD__). __RFC Compliance__ includes any absolute requirements present + -- in RFCs. The __Best Practices__ includes, in addition to RFC + -- recommendations, recommendations found elsewhere or generally accepted. + , not500 + , onlyJsonObjects + , notAllowedContainsAllowHeader + , unauthorizedContainsWWWAuthenticate + , getsHaveCacheControlHeader + , headsHaveCacheControlHeader + -- *** Predicate utilities and types + , (<%>) + , Predicates + , ResponsePredicate(..) + , RequestPredicate(..) -- * Equality testing , serversEqual @@ -28,19 +51,10 @@ module Servant.QuickCheck -- ** Response equality type , ResponseEquality(..) - -- * Property testing - , serverSatisfies - -- ** Predicates - -- *** Useful predicates - , not500 - , onlyJsonObjects - , notAllowedContainsAllowHeader - , unauthorizedContainsWWWAuthenticate - -- *** Predicate utilities and types - , (<%>) - , Predicates - , ResponsePredicate(..) - , RequestPredicate(..) + -- * Test setup helpers + -- | Helpers to setup and teardown @servant@ servers during tests. + , withServantServer + , withServantServerAndContext -- ** Re-exports , BaseUrl(..) diff --git a/src/Servant/QuickCheck/Internal/Predicates.hs b/src/Servant/QuickCheck/Internal/Predicates.hs index 74f88de..b85c480 100644 --- a/src/Servant/QuickCheck/Internal/Predicates.hs +++ b/src/Servant/QuickCheck/Internal/Predicates.hs @@ -1,22 +1,28 @@ +{-# LANGUAGE MultiWayIf #-} module Servant.QuickCheck.Internal.Predicates where import Control.Monad -import Data.Aeson (Object, decode) -import Data.Bifunctor (Bifunctor (..)) -import qualified Data.ByteString.Lazy as LBS -import qualified Data.ByteString as SBS +import Data.Aeson (Object, decode) +import Data.Bifunctor (Bifunctor (..)) +import qualified Data.ByteString as SBS import qualified Data.ByteString.Char8 as SBSC -import Data.CaseInsensitive (mk) -import Data.Either (isRight) -import Data.List.Split (wordsBy) -import Data.Monoid ((<>)) -import Data.Text (Text) -import GHC.Generics (Generic) -import Network.HTTP.Client (Manager, Request, Response, httpLbs, - responseBody, responseStatus, responseHeaders) -import Network.HTTP.Types (status500, status405, status401, parseMethod) +import qualified Data.ByteString.Lazy as LBS +import Data.CaseInsensitive (mk) +import Data.Either (isRight) +import Data.List.Split (wordsBy) +import Data.Maybe (isJust) +import Data.Monoid ((<>)) +import Data.Text (Text) +import GHC.Generics (Generic) +import Network.HTTP.Client (Manager, Request, Response, httpLbs, + method, responseBody, responseHeaders, + responseStatus) +import Network.HTTP.Types (methodGet, methodHead, parseMethod, + status401, renderStdMethod, status405, status500) --- | @500 Internal Server Error@ should be avoided - it may represent some +-- | [__Best Practice__] +-- +-- @500 Internal Server Error@ should be avoided - it may represent some -- issue with the application code, and it moreover gives the client little -- indication of how to proceed or what went wrong. -- @@ -24,7 +30,9 @@ import Network.HTTP.Types (status500, status405, status401, parseMet not500 :: ResponsePredicate Text Bool not500 = ResponsePredicate "not500" (\resp -> not $ responseStatus resp == status500) --- | Returning anything other than an object when returning JSON is considered +-- | [__Best Practice__] +-- +-- Returning anything other than an object when returning JSON is considered -- bad practice, as: -- -- (1) it is hard to modify the returned value while maintaining backwards @@ -66,8 +74,11 @@ getsHaveLastModifiedHeader -} --- | When an HTTP request has a method that is not allowed, a 405 response --- should be returned. Additionally, it is good practice to return an @Allow@ +-- | [__RFC Compliance__] +-- +-- When an HTTP request has a method that is not allowed, +-- a 405 response should be returned. Additionally, it is good practice to +-- return an @Allow@ -- header with the list of allowed methods. -- -- This function checks that every @405 Method Not Allowed@ response contains @@ -163,7 +174,9 @@ linkHeadersAreValid = ResponsePredicate "linkHeadersAreValid" _ -} --- | Any @401 Unauthorized@ response must include a @WWW-Authenticate@ header. +-- | [__RFC Compliance__] +-- +-- Any @401 Unauthorized@ response must include a @WWW-Authenticate@ header. -- -- This function checks that, if a response has status code 401, it contains a -- @WWW-Authenticate@ header. diff --git a/src/Servant/QuickCheck/Internal/QuickCheck.hs b/src/Servant/QuickCheck/Internal/QuickCheck.hs index 062c66f..822c473 100644 --- a/src/Servant/QuickCheck/Internal/QuickCheck.hs +++ b/src/Servant/QuickCheck/Internal/QuickCheck.hs @@ -1,4 +1,3 @@ --- | This module contains wrappers around lower-level functionality. module Servant.QuickCheck.Internal.QuickCheck where import qualified Data.ByteString.Lazy as LBS @@ -8,13 +7,14 @@ import Network.HTTP.Client (Manager, Request, checkStatus, defaultManagerSettings, httpLbs, newManager) import Network.Wai.Handler.Warp (withApplication) -import Servant (HasServer, Server, serve) +import Servant (Context (EmptyContext), HasServer, + Server, serveWithContext) import Servant.Client (BaseUrl (..), Scheme (..)) import System.IO.Unsafe (unsafePerformIO) import Test.Hspec (Expectation, expectationFailure) import Test.QuickCheck (Args (..), Result (..), quickCheckWithResult) -import Test.QuickCheck.Monadic +import Test.QuickCheck.Monadic (assert, forAllM, monadicIO, run) import Servant.QuickCheck.Internal.HasGenRequest import Servant.QuickCheck.Internal.Predicates