content: {-# LANGUAGE CPP, OverloadedStrings #-}
  
  import Control.Applicative   ((<|>))
  import Control.Exception (bracket)
  import Data.Text (Text)
  import Database.MySQL.Simple
  import Database.MySQL.Simple.Param
  import System.Environment    (getEnvironment)
  import Test.Hspec
  import Blaze.ByteString.Builder (toByteString)
  
  import Common                       ()
  import DateTimeSpec                 (dateTimeUnit, dateTimeSpec)
  import CustomTypeSpec               (customTypeUnit, customTypeSpec)
  
  isCI :: IO Bool
  isCI = do
      env <- getEnvironment
      return $ case lookup "TRAVIS" env <|> lookup "CI" env of
                 Just "true" -> True
                 _ -> False
  
  -- This is how to connect to our test database
  testConn :: Bool -> ConnectInfo
  testConn ci = defaultConnectInfo {
                  connectHost     = "127.0.0.1"
                , connectUser     = "test"
                , connectPassword = "test"
                , connectDatabase = "test"
                , connectPort     = if ci then 33306 else 3306
                }
  
  -- Allow tests to do things which would be prevented in strict SQL mode
  withConnection :: (Connection -> IO()) -> IO()
  withConnection action = do
      ci <- isCI
      bracket (do  conn <- connect $ testConn ci
                   _ <- execute_ conn "set session sql_mode=''"
                   return conn
              )
              close
              action
  
  main :: IO ()
  main =
      withConnection $ \conn ->
        hspec $ do
          describe "Database.MySQL.Simple.unitSpec" unitSpec
          describe "Database.MySQL.Simple.integrationSpec" $ integrationSpec conn
          describe "Database.MySQL.Simple.DateTimeSpec" $ do
            dateTimeUnit
            dateTimeSpec conn
          describe "Database.MySQL.Simple.CustomTypeSpec" $ do
            customTypeUnit
            customTypeSpec conn
  
  unitSpec :: Spec
  unitSpec = do
    describe "Param a => Param (In [a]) instance" $ do
      it "renders an empty list correctly" $ do
        let empty :: [Text]
            empty = mempty
  
        case render (In empty) of
          Plain a -> toByteString a `shouldBe` "(null)"
          _       -> expectationFailure "expected a Plain"
  
      it "renders a non-empty list correctly" $ do
        let l :: [Text]
            l = ["foo", "bar"]
  
        case render (In l) of
          Many [Plain _, Escape "foo", Plain _, Escape "bar", Plain _] -> pure ()
          _ -> expectationFailure "expected a Many with specific contents"
  
    describe "splitQuery" $ do
      it "works for a single question mark" $ do
        splitQuery "select * from foo where name = ?"
          `shouldBe`
            ["select * from foo where name = ", ""]
      it "works with a question mark in a string literal" $ do
        splitQuery "select 'hello?'"
          `shouldBe`
            ["select 'hello?'"]
      it "works with many question marks" $ do
        splitQuery "select ? + ? + what from foo where bar = ?"
          `shouldBe`
            ["select ", " + ", " + what from foo where bar = ", ""]
  
  integrationSpec :: Connection -> Spec
  integrationSpec conn = do
    describe "query_" $ do
      it "can connect to a database" $ do
        result <- query_ conn "select 1 + 1"
        result `shouldBe` [Only (2::Int)]
      it "can have question marks in string literals" $ do
        result <- query_ conn "select 'hello?'"
        result `shouldBe` [Only ("hello?" :: Text)]
    describe "query" $ do
      it "can have question marks in string literals" $ do
        result <- query conn "select 'hello?'" ()
        result `shouldBe` [Only ("hello?" :: Text)]
      describe "with too many query params" $ do
        it "should have the right message" $ do
          (query conn "select 'hello?'" (Only ['a']) :: IO [Only Text])
            `shouldThrow`
              (\e -> fmtMessage e == "0 '?' characters, but 1 parameters")
      describe "with too few query params" $ do
        it "should have the right message" $ do
          (query conn "select 'hello?' = ?" () :: IO [Only Text])
            `shouldThrow`
              (\e -> fmtMessage e == "1 '?' characters, but 0 parameters")
    describe "formatQuery" $ do
      it "should not blow up on a question mark in string literal" $ do
        formatQuery conn "select 'hello?'" ()
          `shouldReturn`
            "select 'hello?'"
  