tmp-proc 0.5.0.0 → 0.5.0.1
raw patch · 5 files changed
+261/−33 lines, 5 filesdep ~basedep ~bytestringdep ~textnew-component:exe:readme
Dependency ranges changed: base, bytestring, text, wai, warp, warp-tls
Files
- ChangeLog.md +6/−0
- README.lhs +192/−0
- test/Test/SimpleServer.hs +10/−4
- test/Test/System/TmpProc/WarpSpec.hs +7/−2
- tmp-proc.cabal +46/−27
ChangeLog.md view
@@ -2,6 +2,12 @@ `tmp-proc` uses [PVP Versioning][1]. +## 0.5.0.1 -- 2021-09-30++* Fix use of packaged data in tests+* Introduce some build flags to control how the package builds in CI environments++ ## 0.5.0.0 -- 2021-09-28 * Initial release to hackage
+ README.lhs view
@@ -0,0 +1,192 @@+# tmp-proc++[](https://hackage.haskell.org/package/tmp-proc)+[](https://github.com/adetokunbo/tmp-proc/actions)+[](https://github.com/adetokunbo/tmp-proc/blob/master/tmp-proc/LICENSE)++`tmp-proc` is a small library designed to simplify integration tests that use+services running on docker.++This README contains a _How to_ tutorial on using this library. This tutorial+explains step by step how to specify a docker image as a `tmp proc` and use it in+a test.++__N.B.__ It assumes that docker is installed.++All code below can be compiled and run with the following commands:++```shell+# At the moment (30/09/2021), this is does not consistently run when using cabal+$ stack build tmp-proc+$ stack run readme+```++## Preamble: imports and language extensions++Since this is a literate haskell file, we need to specify all our language+extensions and imports up front.++```haskell+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}++import Test.Hspec++import qualified Data.ByteString.Char8 as C8+import Data.List (foldl')+import Data.Proxy (Proxy (..))+import Data.Text (Text)+import qualified Data.Text as Text+import Network.HTTP.Req++import System.TmpProc (HList (..), HandlesOf, HostIpAddress,+ Pinged (..), Proc (..), ProcHandle (..),+ SvcURI, manyNamed, startupAll, toPinged,+ (&:), ixPing, nameOf, terminateAll)++```+++## Specify a Proc instance++In `tmp-proc`, docker instances are specified by making new instances of the+`Proc` typeclass.++For this tutorial, we'll test the famous [http-bin](https://httpbin.org)+service. Although it's an online service in it's own right, it is also available+as a docker image.+++```haskell+data HttpBinLhs = HttpBinLhs++instance Proc HttpBinLhs where+ type Image HttpBinLhs = "kennethreitz/httpbin"+ type Name HttpBinLhs = "http-bin-lhs"++ uriOf ip = "http://" <> C8.pack (Text.unpack ip) <> "/"+ runArgs = []+ reset _ = pure ()+ ping = pingImpl++```++A `Proc` instance specifies both an `Image` and `Name`.+- The `Image` corresponds to the docker image that needs to be run+- The `Name` is a label that needs to be unique; it is used an alternate index for the Proc instance.++The instance also specifies a number of useful typeclass functions; only `ping`+will be covered in this tutorial, but the others all have important roles.+++```haskell++pingImpl :: ProcHandle a -> IO Pinged+pingImpl handle = toPinged @HttpException Proxy $ do+ gotStatus <- runReq defaultHttpConfig $ do+ r <- req GET (handleUrl handle "/status/200") NoReqBody ignoreResponse $ mempty+ pure $ responseStatusCode r+ if (gotStatus == 200) then pure OK else pure NotOK+++handleUrl :: ProcHandle a -> Text -> Url 'Http+handleUrl handle urlPath = foldl' (/:) (http $ hAddr handle)+ $ Text.splitOn "/" $ Text.dropWhile (== '/') urlPath+++```++The `pingImpl` used by the `Proc` instance above is implemented next.++Each `Proc` instance must provide a valid `ping` implementation, `tmp-proc` uses+`ping` to determine when the `Proc's` service is ready for use in the test.++## Using the Proc+++```haskell++spec :: Spec+spec = describe ("Tmp.Proc: " ++ Text.unpack (nameOf HttpBinLhs)) $ do+ beforeAll (startupAll $ HttpBinLhs &: HNil) $ afterAll terminateAll $ do+ context "When accessing the services in the list of test tmp procs" $ do++ context "ixPing" $ do++ it "should succeed when accessing a Proc by name" $ \handles+ -> ixPing @"http-bin-lhs" Proxy handles `shouldReturn` OK++ it "should succeed when accessing a Proc by type" $ \handles+ -> ixPing @HttpBinLhs Proxy handles `shouldReturn` OK++```++With just this, it's now possible to write a simple test showing various+features of `tmp-proc`. E.g,++- `hspec` launches `Procs` during test setup++ - this results in an `HList` of `ProcHandle` types being passed to each test.++ - (In this example, the HList has only one `Proc`. `startupAll` allows for+ many `Procs` to started, each of different type. This is possible because+ `startupAll` acts on and returns a heteregenous list (or `HList`) rather+ than the usual `List` type.++ - `startupAll` takes an `HList` of `Procs` and returns an `HList` of+ corresponding `ProcHandle` types, first ensuring that all the corresponding+ docker services start up ok++- once setup succeeds, each test is passed the `ProcHandles` created by+ `startupAll`++ - this is powered by a feature of [hspec][2], it's through use of `hspec's`+ `beforeAll` hook that enables this++ - `startupAll` is just one example of a `tmp-proc` combinator meshing well with+ the test frameworks' combinators.++ - the `startAll` and `terminateAll` functions used here also work with+ `tasty's` [withResource][6]++ - `tmp-proc` provides other functions that work with other hooks in [hspec][1]++- the test cases here show the way that `tmp-proc` functions work with an+ `HList` of `ProcHandles`++ - `ixPing` uses [TypeApplications][7] with an index type to identify the+ 'ProcHandle' to ping++ - `tmp-proc` provides similar functions that enable access to attributes+ of one or many `ProcHandle`.++ - Though not shown here, as well as accessing a `ProcHandle's` attributes,+ test code may also to access the corresponding service using a+ `Connection` type specific to that service.++ - __N.B.__ these test cases are completely unrealistic; there should be no+ reason to use `ixPing` in a normal test case! For slightly more realism,+ please take a look at the [example package][4].+++## Run the Spec++```haskell++main :: IO ()+main = hspec spec++```+++[1]: https://hackage.haskell.org/package/wai+[2]: https://hspec.github.io+[3]: https://hackage.haskell.org/package/tasty+[4]: https://github.com/adetokunbo/tmp-proc/tree/master/tmp-proc-example+[5]: https://github.com/adetokunbo/tmp-proc/tree/master/tmp-proc+[6]: https://hackage.haskell.org/package/tasty-1.4.2/docs/Test-Tasty.html#v:withResource+[7]: https://typeclasses.com/ghc/type-applications
test/Test/SimpleServer.hs view
@@ -22,7 +22,17 @@ import qualified Network.Wai.Handler.Warp as Warp import qualified Network.Wai.Handler.WarpTLS as Warp +import Paths_tmp_proc (getDataFileName) ++-- | The settings used in the integration tests+defaultTLSSettings :: IO Warp.TLSSettings+defaultTLSSettings =+ Warp.tlsSettings+ <$> (getDataFileName "test_certs/certificate.pem")+ <*> (getDataFileName "test_certs/key.pem")++ -- | Determine the status from a Get on localhost. statusOfGet :: Warp.Port -> Text -> IO Int statusOfGet p path = runReq defaultHttpConfig $ do@@ -46,10 +56,6 @@ localHttpsUrl :: Text -> Url 'Https localHttpsUrl p = foldl' (/:) (https "localhost") $ Text.splitOn "/" $ Text.dropWhile (== '/') p---defaultTLSSettings :: Warp.TLSSettings-defaultTLSSettings = Warp.tlsSettings "test_certs/certificate.pem" "test_certs/key.pem" mkSimpleTLSManager :: IO HC.Manager
test/Test/System/TmpProc/WarpSpec.hs view
@@ -47,8 +47,11 @@ setupBeforeAll :: IO (ServerHandle '[HttpBinTest]) setupBeforeAll = runServer testProcs testApp + setupBeforeAllTls :: IO (ServerHandle '[HttpBinTest])-setupBeforeAllTls = runTLSServer defaultTLSSettings testProcs testApp+setupBeforeAllTls = do+ tls <- defaultTLSSettings+ runTLSServer tls testProcs testApp suffixAround, suffixBeforeAll, prefixHttp, prefixHttps :: String@@ -83,7 +86,9 @@ setupAroundTls :: ((HandlesOf '[HttpBinTest], Int) -> IO a) -> IO a-setupAroundTls = testWithTLSApplication defaultTLSSettings testProcs testApp+setupAroundTls cont = do+ tls <- defaultTLSSettings+ testWithTLSApplication tls testProcs testApp cont aroundSpec :: Spec
tmp-proc.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: tmp-proc-version: 0.5.0.0+version: 0.5.0.1 synopsis: Run 'tmp' processes in integration tests description: @tmp-proc@ runs services in docker containers for use in integration tests.@@ -27,11 +27,13 @@ maintainer: adetokunbo@users.noreply.github.com category: testing, docker bug-reports: https://github.com/adetokunbo/tmp-proc/issues+homepage: https://github.com/adetokunbo/tmp-proc/tree/master/tmp-proc#tmp-proc build-type: Simple-extra-source-files:- ChangeLog.md+data-files: test_certs/*.csr test_certs/*.pem+extra-source-files:+ ChangeLog.md tested-with: GHC == 8.10.5 @@ -67,6 +69,8 @@ test-suite integration-test type: exitcode-stdio-1.0 main-is: Spec.hs+ autogen-modules: Paths_tmp_proc+ other-modules: Test.Hspec.TmpProc Test.HttpBin@@ -74,6 +78,7 @@ Test.System.TmpProc.Hspec Test.System.TmpProc.HttpBinSpec Test.System.TmpProc.WarpSpec+ Paths_tmp_proc hs-source-dirs: test build-depends:@@ -96,7 +101,15 @@ ghc-options: -threaded -rtsopts -with-rtsopts=-N -fno-ignore-asserts -Wall +Flag use-doc-tests+ description: Include the doctests in the package tests+ default: False+ test-suite doctests+ if flag(use-doc-tests)+ buildable: True+ else+ buildable: False type: exitcode-stdio-1.0 main-is: Main.hs build-depends:@@ -108,28 +121,34 @@ default-language: Haskell2010 ghc-options: -threaded --- This is not currently working in the nix build environment.------ executable readme--- if os(windows)--- buildable: False --- build-tool-depends: markdown-unlit:markdown-unlit--- ghc-options: -pgmL markdown-unlit--- main-is: README.lhs--- default-language: Haskell2010--- build-depends:--- , base--- , bytestring--- , connection--- , data-default--- , hspec--- , http-client--- , http-client-tls--- , http-types--- , req--- , text--- , tmp-proc--- , wai--- , warp--- , warp-tls+Flag build-the-readme+ description: Allow the readme to build+ default: False+++executable readme+ if os(windows) || !flag(build-the-readme)+ buildable: False+ else+ buildable: True++ build-tool-depends: markdown-unlit:markdown-unlit+ ghc-options: -pgmL markdown-unlit -threaded -rtsopts -with-rtsopts=-N+ main-is: README.lhs+ default-language: Haskell2010+ build-depends:+ , base+ , bytestring+ , connection+ , data-default+ , hspec+ , http-client+ , http-client-tls+ , http-types+ , req+ , text+ , tmp-proc+ , wai+ , warp+ , warp-tls