wreq-patchable-1.0.0.0: tests/AWS/DynamoDB.hs
{-# LANGUAGE OverloadedLists, OverloadedStrings #-}
module AWS.DynamoDB (tests) where
import AWS.Aeson
import Control.Concurrent (threadDelay)
import Control.Lens hiding ((.=))
import Data.Aeson.Lens (key, _String, values, _Double)
import Data.Text as T (pack)
import Network.Wreq
import System.Timeout (timeout)
import Test.Framework (Test, testGroup)
import Test.Framework.Providers.HUnit (testCase)
import Test.HUnit (assertBool, assertFailure)
-- FIXME: retry create call in case the table is in DELETING state
-- from a previous test run (error 'Table already exists: ...'). For
-- now 'create' testcase and all others will fails. Rerun when ongoing
-- delete operation is complete.
tests :: String -> String -> Options -> Test
tests prefix region baseopts = testGroup "dynamodb" [
testCase "createTable" $ createTable prefix region baseopts
, testCase "listTables" $ listTables prefix region baseopts
, testCase "awaitTableActive" $ awaitTableActive prefix region baseopts
, testCase "putItem" $ putItem prefix region baseopts
, testCase "getItem" $ getItem prefix region baseopts
, testCase "deleteItem" $ deleteItem prefix region baseopts
, testCase "deleteTable" $ deleteTable prefix region baseopts -- call last
]
createTable :: String -> String -> Options -> IO ()
createTable prefix region baseopts = do
let opts = baseopts
& header "X-Amz-Target" .~ ["DynamoDB_20120810.CreateTable"]
& header "Content-Type" .~ ["application/x-amz-json-1.0"]
r <- postWith opts (url region) $
object [
"TableName" .= string (prefix ++ tablename),
"KeySchema" .= [
object ["AttributeName" .= "name", "KeyType" .= "HASH"],
object ["AttributeName" .= "age", "KeyType" .= "RANGE"]
],
"AttributeDefinitions" .= [
object ["AttributeName" .= "name", "AttributeType" .= "S"],
object ["AttributeName" .= "age", "AttributeType" .= "S"]
],
"ProvisionedThroughput" .= object [
"ReadCapacityUnits" .= 1,
"WriteCapacityUnits" .= 1
]
]
assertBool "createTables 200" $ r ^. responseStatus . statusCode == 200
assertBool "createTables OK" $ r ^. responseStatus . statusMessage == "OK"
assertBool "createTables status CREATING" $
r ^. responseBody . key "TableDescription" . key "TableStatus" . _String == "CREATING"
assertBool "createTables no items in new table" $
r ^? responseBody . key "TableDescription" . key "ItemCount" . _Double == Just 0
listTables :: String -> String -> Options -> IO ()
listTables prefix region baseopts = do
let opts = baseopts
& header "X-Amz-Target" .~ ["DynamoDB_20120810.ListTables"]
& header "Content-Type" .~ ["application/x-amz-json-1.0"]
-- FIXME avoid limit to keep tests from failing if there are > tables?
r <- postWith opts (url region) $ object ["Limit" .= 100]
assertBool "listTables 200" $ r ^. responseStatus . statusCode == 200
assertBool "listTables OK" $ r ^. responseStatus . statusMessage == "OK"
assertBool "listTables contains test table" $
elem (T.pack $ prefix ++ tablename)
(r ^.. responseBody . key "TableNames" . values . _String)
awaitTableActive :: String -> String -> Options -> IO ()
awaitTableActive prefix region baseopts = do
let dur = 45 -- typically ACTIVE in 20s or less (us-west-2, Sept 2014)
res <- timeout (dur*1000*1000) check
case res of
Nothing ->
assertFailure $ "timeout: table not ACTIVE after " ++ show dur ++ "s"
Just () ->
return () -- PASS
where
check = do
let opts = baseopts
& header "X-Amz-Target" .~ ["DynamoDB_20120810.DescribeTable"]
& header "Content-Type" .~ ["application/x-amz-json-1.0"]
r <- postWith opts (url region) $
object ["TableName" .= string (prefix ++ tablename)]
assertBool "awaitTableActive 200" $ r ^. responseStatus . statusCode == 200
assertBool "awaitTableActive OK" $ r ^. responseStatus . statusMessage == "OK"
-- Prelude.putStr "."
case r ^. responseBody . key "Table" . key "TableStatus" . _String of
"ACTIVE" ->
return ()
_ -> do
threadDelay $ 5*1000*1000 -- 5 sleep
check
deleteTable :: String -> String -> Options -> IO ()
deleteTable prefix region baseopts = do
let opts = baseopts
& header "X-Amz-Target" .~ ["DynamoDB_20120810.DeleteTable"]
& header "Content-Type" .~ ["application/x-amz-json-1.0"]
r <- postWith opts (url region) $
object ["TableName" .= string (prefix ++ tablename)]
assertBool "deleteTable 200" $ r ^. responseStatus . statusCode == 200
assertBool "deleteTable OK" $ r ^. responseStatus . statusMessage == "OK"
putItem :: String -> String -> Options -> IO ()
putItem prefix region baseopts = do
let opts = baseopts
& header "X-Amz-Target" .~ ["DynamoDB_20120810.PutItem"]
& header "Content-Type" .~ ["application/x-amz-json-1.0"]
r <- postWith opts (url region) $
object [
"TableName" .= string (prefix ++ tablename),
"Item" .= object [
"name" .= object ["S" .= "someone"],
"age" .= object ["S" .= "whatever"],
"bar" .= object ["S" .= "baz"]
]
]
assertBool "putItem 200" $ r ^. responseStatus . statusCode == 200
assertBool "putItem OK" $ r ^. responseStatus . statusMessage == "OK"
getItem :: String -> String -> Options -> IO ()
getItem prefix region baseopts = do
let opts = baseopts
& header "X-Amz-Target" .~ ["DynamoDB_20120810.GetItem"]
& header "Content-Type" .~ ["application/x-amz-json-1.0"]
r <- postWith opts (url region) $
object [
"TableName" .= string (prefix ++ tablename),
"Key" .= object [
"name" .= object ["S" .= "someone"],
"age" .= object ["S" .= "whatever"]
],
"AttributesToGet" .= ["bar"],
"ConsistentRead" .= true,
"ReturnConsumedCapacity" .= "TOTAL"
]
assertBool "getItem 200" $ r ^. responseStatus . statusCode == 200
assertBool "getItem OK" $ r ^. responseStatus . statusMessage == "OK"
assertBool "getItem baz value is bar" $
r ^. responseBody . key "Item" . key "bar" . key "S" . _String == "baz"
deleteItem :: String -> String -> Options -> IO ()
deleteItem prefix region baseopts = do
let opts = baseopts
& header "X-Amz-Target" .~ ["DynamoDB_20120810.DeleteItem"]
& header "Content-Type" .~ ["application/x-amz-json-1.0"]
r <- postWith opts (url region) $
object [
"TableName" .= string (prefix ++ tablename),
"Key" .= object [
"name" .= object ["S" .= "someone"],
"age" .= object ["S" .= "whatever"]
],
"ReturnValues" .= "ALL_OLD"
]
assertBool "getItem 200" $ r ^. responseStatus . statusCode == 200
assertBool "getItem OK" $ r ^. responseStatus . statusMessage == "OK"
url :: String -> String
url region =
"https://dynamodb." ++ region ++ ".amazonaws.com/"
tablename :: String
tablename =
"test"