hal-0.4.9: test/Spec.hs
import AWS.Lambda.Context (ClientApplication (..),
ClientContext (..),
CognitoIdentity (..),
LambdaContext (..))
import qualified Gen.Header as Header
import qualified AWS.Lambda.Events.ApiGateway.ProxyRequest.Spec as ProxyRequest
import qualified AWS.Lambda.Events.ApiGateway.ProxyResponse.Spec as ProxyResponse
import qualified AWS.Lambda.Events.Kafka.Spec as Kafka
import AWS.Lambda.Internal (StaticContext (..))
import AWS.Lambda.RuntimeClient.Internal (eventResponseToNextData)
import Data.Aeson (Value (Null))
import qualified Data.CaseInsensitive as CI
import Data.Map (singleton)
import Data.Semigroup ((<>))
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Network.HTTP.Client.Internal (Response (..))
import Network.HTTP.Types (Header)
import Test.Hspec (describe, it, shouldBe,
shouldStartWith, specify)
import Test.Hspec.Hedgehog (hedgehog)
import Test.Hspec.Runner (hspec)
main :: IO ()
main =
hspec $ do
describe "Events" $ do
describe "ApiGateway" $ do
describe "ProxyRequest" ProxyRequest.spec
describe "ProxyResponse" ProxyResponse.spec
describe "Kafka" Kafka.spec
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")
describe "properties" $ do
specify "Header.unfoldCase preserves equality" $
hedgehog $ do
-- CI only promises equality over the Latin-1 set
s <- forAll $ CI.mk <$> Gen.text (Range.linear 1 100) Gen.latin1
t <- forAll $ Header.unfoldCase s
s === t
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