packages feed

hal-0.4.7: test/Spec.hs

import           AWS.Lambda.Context                (ClientApplication (..),
                                                    ClientContext (..),
                                                    CognitoIdentity (..),
                                                    LambdaContext (..))
import           AWS.Lambda.Internal               (StaticContext (..))
import           AWS.Lambda.RuntimeClient.Internal (eventResponseToNextData)
import           Data.Aeson                        (Value (Null))
import           Data.Map                          (singleton)
import           Data.Semigroup                    ((<>))
import           Data.Time.Clock.POSIX             (posixSecondsToUTCTime)
import           Network.HTTP.Client.Internal      (Response (..))
import           Network.HTTP.Types                (Header)
import           Test.Hspec                        (describe, it, shouldBe,
                                                    shouldStartWith)
import           Test.Hspec.Runner                 (hspec)

main :: IO ()
main =
  hspec $ do
    describe "Event Response Data" $ do
      let staticContext =
            StaticContext
              { functionName = "name"
              , functionVersion = "version"
              , functionMemorySize = 128
              , logGroupName = "logGroupName"
              , logStreamName = "logStreamName"
              }
      let basicValidHeaders =
            [ ("Lambda-Runtime-Aws-Request-Id", "abc")
            , ("Lambda-Runtime-Trace-Id", "123")
            , ("Lambda-Runtime-Invoked-Function-Arn", "arn")
            , ("Lambda-Runtime-Deadline-Ms", "12332000")
            ]
      it "Works given valid inputs" $ do
        let inputBody = Null
        let response = minResponse basicValidHeaders inputBody
        let (id, outputBody, context) =
              eventResponseToNextData staticContext response
        id `shouldBe` "abc"
        outputBody `shouldBe` inputBody
        context `shouldBe`
          (Right
             (LambdaContext
                { functionName = "name"
                , functionVersion = "version"
                , functionMemorySize = 128
                , logGroupName = "logGroupName"
                , logStreamName = "logStreamName"
                , awsRequestId = "abc"
                , invokedFunctionArn = "arn"
                , xRayTraceId = "123"
                , deadline = posixSecondsToUTCTime 12332
                , clientContext = Nothing
                , identity = Nothing
                }))
      it "has clientContext if it was provided" $ do
        let headers =
              ( "Lambda-Runtime-Client-Context"
              , "{ \"client\": { \"appTitle\": \"title\", \"appVersionName\": \"versionName\", \"appVersionCode\": \"versionCode\", \"appPackageName\": \"packageName\" }, \"custom\": { \"key1\": \"value1\" }, \"environment\": { \"key2\": \"value2\" } }") :
              basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        fmap clientContext context `shouldBe`
          (Right
             (Just
                (ClientContext
                   { client =
                       ClientApplication
                         { appTitle = "title"
                         , appVersionName = "versionName"
                         , appVersionCode = "versionCode"
                         , appPackageName = "packageName"
                         }
                   , custom = singleton "key1" "value1"
                   , environment = singleton "key2" "value2"
                   })))
      it "fails to construct the Context if the client context can't be parsed" $ do
        let headers =
              ("Lambda-Runtime-Client-Context", "{}") : basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        let msg = either id (const (error "Was able to parse a context that should have failed!")) context
        msg `shouldStartWith`
          "Runtime Error: Unable to decode Context from event response.\nCould not JSON decode header Lambda-Runtime-Client-Context: "
      it
        "fails to construct the Context if there are two client context headers" $ do
        let headers =
              [ ("Lambda-Runtime-Client-Context", "{}")
              , ("Lambda-Runtime-Client-Context", "{}")
              ] <>
              basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        fmap clientContext context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nToo many values for header Lambda-Runtime-Client-Context")
      it "has identity if it was provided" $ do
        let headers =
              ( "Lambda-Runtime-Cognito-Identity"
              , "{ \"identityId\": \"identityId\", \"identityPoolId\": \"identityPoolId\" }") :
              basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        fmap identity context `shouldBe`
          (Right
             (Just
                (CognitoIdentity
                   { identityId = "identityId"
                   , identityPoolId = "identityPoolId"
                   })))
      it
        "fails to construct the Context if the cognito identity can't be parsed" $ do
        let headers =
              ("Lambda-Runtime-Cognito-Identity", "{}") : basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        let msg = either id (const (error "Was able to parse a context that should have failed!")) context
        msg `shouldStartWith`
          "Runtime Error: Unable to decode Context from event response.\nCould not JSON decode header Lambda-Runtime-Cognito-Identity: "
      it
        "fails to construct the Context if there are two cognito identity headers" $ do
        let headers =
              [ ("Lambda-Runtime-Cognito-Identity", "{}")
              , ("Lambda-Runtime-Cognito-Identity", "{}")
              ] <>
              basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        fmap clientContext context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nToo many values for header Lambda-Runtime-Cognito-Identity")
      it "fails to create the context if trace Id is not provided" $ do
        let headers =
              [ ("Lambda-Runtime-Aws-Request-Id", "abc")
              , ("Lambda-Runtime-Invoked-Function-Arn", "arn")
              , ("Lambda-Runtime-Deadline-Ms", "12332000")
              ]
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nMissing response header Lambda-Runtime-Trace-Id")
      it "fails to create the context if trace id has multiple values" $ do
        let headers = ("Lambda-Runtime-Trace-Id", "123") : basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nToo many values for header Lambda-Runtime-Trace-Id")
      it "fails to create the context if function ARN is not provided" $ do
        let headers =
              [ ("Lambda-Runtime-Aws-Request-Id", "abc")
              , ("Lambda-Runtime-Trace-Id", "123")
              , ("Lambda-Runtime-Deadline-Ms", "12332000")
              ]
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nMissing response header Lambda-Runtime-Invoked-Function-Arn")
      it "fails to create the context if function ARN has multiple values" $ do
        let headers =
              ("Lambda-Runtime-Invoked-Function-Arn", "arn") : basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nToo many values for header Lambda-Runtime-Invoked-Function-Arn")
      it "fails to create the context if a deadline is not provided" $ do
        let headers =
              [ ("Lambda-Runtime-Aws-Request-Id", "abc")
              , ("Lambda-Runtime-Trace-Id", "123")
              , ("Lambda-Runtime-Invoked-Function-Arn", "arn")
              ]
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nMissing response header Lambda-Runtime-Deadline-Ms")
      it "fails to create the context if the deadline has multiple values" $ do
        let headers =
              ("Lambda-Runtime-Deadline-Ms", "12332000") : basicValidHeaders
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nToo many values for header Lambda-Runtime-Deadline-Ms")
      it "fails to create the context if the deadline is not a valid timestamp" $ do
        let headers =
              [ ("Lambda-Runtime-Aws-Request-Id", "abc")
              , ("Lambda-Runtime-Trace-Id", "123")
              , ("Lambda-Runtime-Invoked-Function-Arn", "arn")
              , ("Lambda-Runtime-Deadline-Ms", "not a timestamp")
              ]
        let (_, _, context) =
              eventResponseToNextData staticContext (minJsonResponse headers)
        context `shouldBe`
          (Left
             "Runtime Error: Unable to decode Context from event response.\nCould not parse deadline")

minResponse :: [Header] -> a -> Response a
minResponse headers body =
  Response
    { responseStatus = undefined
    , responseVersion = undefined
    , responseHeaders = headers
    , responseBody = body
    , responseCookieJar = undefined
    , responseClose' = undefined
    }

minJsonResponse :: [Header] -> Response Value
minJsonResponse = flip minResponse Null