mismi-s3-0.0.2: test/Test/IO/Mismi/S3/Commands.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PackageImports #-}
module Test.IO.Mismi.S3.Commands where
import Control.Concurrent (threadDelay)
import Control.Monad.Catch (catchAll, throwM)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Except (runExceptT)
import "cryptohash" Crypto.Hash
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Data.Either (isRight, isLeft)
import qualified Data.List as L
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.IO as T
import Control.Lens ((^.), to)
import Control.Monad (replicateM_)
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Mismi.S3
import qualified Mismi.S3.Amazonka as A
import qualified Mismi.S3.Unsafe as Unsafe
import P
import qualified System.Directory as D
import System.FilePath ((</>))
import qualified System.FilePath as F
import System.IO (withFile, IOMode (..), hFileSize, putStrLn)
import System.IO.Error (userError)
import Test.Mismi.Amazonka (sendMultipart, newMultipart)
import Test.Mismi.S3
import qualified Test.Mismi.S3.Core.Gen as Gen
import Mismi.S3.Internal.Parallel (RunError (..))
prop_exists :: Property
prop_exists =
withTests 2 . property . liftAWS $ do
a <- newAddress
lift $ Unsafe.write a ""
result <- lift $ exists a
result === True
prop_exists_empty :: Property
prop_exists_empty =
withTests 2 . property . liftAWS $ do
a <- newAddress
result <- lift $ exists a
result === False
prop_exists_failure :: Property
prop_exists_failure =
withTests 2 . property . liftAWS $ do
a <- newAddress
e <- lift $ exists a
e === False
prop_exists_prefix :: Property
prop_exists_prefix =
withTests 2 . property . liftAWS $ do
k <- forAll $ Gen.genKey
a <- newAddress
lift $ Unsafe.write (withKey (// k) a) ""
e <- lift $ existsPrefix a
e === True
prop_exists_prefix_missing :: Property
prop_exists_prefix_missing =
withTests 2 . property . liftAWS $ do
a <- newAddress
lift $ Unsafe.write a ""
e <- lift $ existsPrefix a
e === False
prop_exists_prefix_key :: Property
prop_exists_prefix_key =
withTests 2 . property . liftAWS $ do
a <- newAddress
e <- lift $ existsPrefix a
e === False
prop_headObject :: Property
prop_headObject =
withTests 2 . property . liftAWS $ do
a <- newAddress
h <- lift $ headObject a
h === Nothing
prop_getObjects_empty :: Property
prop_getObjects_empty =
withTests 2 . property . liftAWS $ do
a <- newAddress
objs <- lift $ getObjectsRecursively $ a
objs === []
prop_getObjectsR :: Property
prop_getObjectsR = -- d p1 p2 = p1 /= p2 ==> testAWS $ do
withTests 2 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.constant 5 15) Gen.alphaNum
items <- fmap toList . forAll $ Gen.set (Range.constant 2 2) Gen.genKey
(p1, p2) <- case items of
p1 : p2 : [] ->
pure (p1, p2)
_ ->
annotate "Invariant generator." >> failure
root <- newAddress
let
keys = [p1, p2 // p1, p2 // p2]
lift . forM_ keys $ \k ->
Unsafe.write (withKey (// k) root) d
objs <- lift $ getObjectsRecursively root
on (===) L.sort ((^. A.oKey . to A.toText) <$> objs) (unKey . (//) (key root) <$> keys)
-- TODO This is incredible slow
prop_pagination_list :: Property
prop_pagination_list =
-- TODO
withTests 0 . property . liftAWS $ do
m <- forAll $ Gen.text (Range.linear 10 15) Gen.alphaNum
n <- forAll $ Gen.int (Range.linear 1000 1500)
a <- newAddress
lift . forM_ [1..n] $ \n' ->
Unsafe.write (withKey(// Key (m <> T.pack (show n'))) a) ""
r' <- lift $ list a
length r' === n
prop_size :: Property
prop_size =
withTests 10 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
lift $ Unsafe.write a d
i <- lift $ size a
i === (Just . fromIntegral . BS.length $ T.encodeUtf8 d)
prop_size_failure :: Property
prop_size_failure =
withTests 2 . property . liftAWS $ do
a <- newAddress
i <- lift $ size a
i === Nothing
prop_size_recursively :: Property
prop_size_recursively =
withTests 2 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
lift $ Unsafe.write a d
r <- lift $ sizeRecursively (a { key = dirname $ key a })
r === [Sized (fromIntegral . BS.length $ T.encodeUtf8 d) a]
-- TODO This is incredible slow
prop_concat :: Property
prop_concat =
withTests 1 . property . liftAWS $ do
a <- newAddress
b <- newAddress
c <- newAddress
f <- newFilePath
let
s = f </> T.unpack "fred"
d = f </> T.unpack "down"
bs10k = BS.concat $ L.replicate 10000 "fred"
liftIO $ withFile s WriteMode $ \h ->
replicateM_ 1000 (BS.hPut h bs10k)
lift $ Unsafe.upload s a
lift $ Unsafe.upload s b
r <- lift . runExceptT $ concatMultipart Fail 1 [a, b] c
() <- either (fail . show . renderConcatError) pure r
lift $ Unsafe.download c d
s' <- liftIO $ LBS.readFile s
d' <- liftIO $ LBS.readFile d
sha1 (LBS.concat [s', s']) === sha1 d'
prop_concat_empty_input :: Property
prop_concat_empty_input =
withTests 2 . property . liftAWS $ do
a <- newAddress
r <- lift . runExceptT $ concatMultipart Fail 1 [] a
case r of
Left NoInputFiles ->
success
_ ->
annotate "concat didn't fail correctly" >> failure
prop_concat_empty_input_files :: Property
prop_concat_empty_input_files =
withTests 2 . property . liftAWS $ do
a <- newAddress
b <- newAddress
lift $ Unsafe.write a ""
r <- lift . runExceptT $ concatMultipart Fail 1 [a] b
case r of
Left NoInputFilesWithData ->
success
_ ->
annotate "concat didn't fail correctly" >> failure
prop_copy :: Property
prop_copy =
withTests 2 . property . liftAWS $ do
t <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
b <- newAddress
lift $ Unsafe.write a t
lift $ either (fail . T.unpack . renderCopyError) pure =<< runExceptT (copy a b)
a' <- lift $ read a
b' <- lift $ read b
a' === b'
prop_copy_missing :: Property
prop_copy_missing =
withTests 2 . property . liftAWS $ do
a <- newAddress
r <- lift . runExceptT $ copy a a
case r of
Left (CopySourceMissing b) ->
a === b
_ ->
annotate "Copy didn't fail correctly" >> failure
prop_copy_overwrite :: Property
prop_copy_overwrite =
withTests 2 . property . liftAWS $ do
t <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
t' <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
b <- newAddress
lift $ Unsafe.write a t
lift $ Unsafe.write b t'
lift $ either (fail . T.unpack . renderCopyError) pure =<< runExceptT (copyWithMode Overwrite a b)
b' <- lift $ read b
b' === Just t
prop_copy_fail :: Property
prop_copy_fail =
withTests 2 . property . liftAWS $ do
t <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
b <- newAddress
lift $ Unsafe.write a t
lift $ Unsafe.write b t
r <- lift . runExceptT $ copyWithMode Fail a b
case r of
Left (CopyDestinationExists z) ->
b === z
_ ->
annotate "Copy didn't failure correctly" >> failure
prop_copy_multipart :: Property
prop_copy_multipart =
withTests 1 . property . liftAWS $ do
c <- forAll $ Gen.text (Range.linear 10 20) Gen.alphaNum
m <- forAll $ Gen.text (Range.linear 20 30) Gen.alphaNum
bs <- forAll $ Gen.utf8 (Range.linear 1 100) Gen.unicodeAll
f <- newFilePath
a' <- newAddress
let
a = withKey (// Key c) a'
b = withKey (// Key m) a'
s = f </> T.unpack c
d = f </> T.unpack m
-- create large file to copy
liftIO $ D.createDirectoryIfMissing True f
liftIO $ withFile s WriteMode $ \h ->
replicateM_ 1000 (LBS.hPut h (LBS.fromChunks . return $ (BS.concat . L.replicate 10000 $ bs)))
liftIO . putStrLn $ "Generated file"
lift $ Unsafe.upload s a
liftIO . putStrLn $ "Uploaded file"
liftIO . putStrLn $ "Running copy ..."
lift $ either (fail . T.unpack . renderCopyError) pure =<< runExceptT (copy a b)
liftIO . putStrLn $ "Done copy"
-- compare
lift $ either (fail . show) pure =<< runExceptT (download b d)
liftIO . putStrLn $ "Done download"
s' <- liftIO $ LBS.readFile s
d' <- liftIO $ LBS.readFile d
sha1 s' === sha1 d'
prop_move :: Property
prop_move =
withTests 2 . property . liftAWS $ do
t <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
s <- newAddress
d <- newAddress
lift $ Unsafe.write s t
lift $ either (fail . T.unpack . renderCopyError) pure =<< runExceptT (move s d)
es <- lift $ exists s
ed <- lift $ exists d
(es, ed) === (False, True)
prop_upload_mode :: Property
prop_upload_mode =
withTests 2 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
l <- forAll $ genLocalPath
m <- forAll $ Gen.genWriteMode
p <- newFilePath
a <- newAddress
let
t = p </> localPath l
liftIO . D.createDirectoryIfMissing True $ F.takeDirectory t
liftIO $ T.writeFile t d
lift $ Unsafe.uploadWithMode m t a
r <- lift $ read a
r === Just d
prop_upload_overwrite :: Property
prop_upload_overwrite =
withTests 2 . property . liftAWS $ do
d1 <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
d2 <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
l <- forAll $ genLocalPath
p <- newFilePath
a <- newAddress
let t = p </> localPath l
liftIO . D.createDirectoryIfMissing True $ F.takeDirectory t
liftIO $ T.writeFile t d1
lift $ Unsafe.uploadWithMode Fail t a
liftIO $ T.writeFile t d2
lift $ Unsafe.uploadWithMode Overwrite t a
r <- lift $ read a
r === Just d2
prop_upload_fail :: Property
prop_upload_fail =
withTests 2 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
l <- forAll $ genLocalPath
p <- newFilePath
a <- newAddress
let t = p </> localPath l
liftIO . D.createDirectoryIfMissing True $ F.takeDirectory t
liftIO $ T.writeFile t d
lift $ Unsafe.uploadWithMode Fail t a
r <- lift . runExceptT $ uploadWithMode Fail t a
case r of
Left (UploadDestinationExists _) ->
success
_ ->
annotate "Upload succeded but should have failed" >> failure
prop_upload :: Property
prop_upload =
withTests 2 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
l <- forAll $ genLocalPath
p <- newFilePath
a <- newAddress
let t = p </> localPath l
liftIO . D.createDirectoryIfMissing True $ F.takeDirectory t
liftIO $ T.writeFile t d
lift $ Unsafe.upload t a
r <- lift $ read a
r === Just d
prop_upload_multipart :: Property
prop_upload_multipart =
withTests 2 . property . liftAWS $ do
bs <- forAll $ Gen.utf8 (Range.linear 0 100) Gen.unicodeAll
l <- forAll $ genLocalPath
p <- newFilePath
a <- newAddress
let t = p </> localPath l
liftIO . D.createDirectoryIfMissing True $ F.takeDirectory t
liftIO $ withFile t WriteMode $ \h ->
replicateM_ 1000 (LBS.hPut h (LBS.fromChunks . return $ (BS.concat . L.replicate 10000 $ bs)))
lift $ Unsafe.upload t a
result <- lift $ exists a
assert result
prop_abort_multipart :: Property
prop_abort_multipart =
withTests 2 . property . liftAWS $ do
(a, i) <- newMultipart
lift $ sendMultipart "" a 1 i
l <- lift $ listMultiparts (bucket a)
let
abortCheck :: Text -> Bucket -> Int -> A.MultipartUpload -> AWS ()
abortCheck x b n u = do
abortMultipart b u
r <- listMultiparts b
unless (n <= (0 :: Int) || L.null (findMultiparts x r)) $ do
liftIO $ threadDelay 500000
abortCheck x b (n-1) u
lift . forM_ (findMultiparts i l) $
abortCheck i (bucket a) 3
r <- lift $ listMultiparts (bucket a)
(L.filter (== Just i) . fmap (^. A.muUploadId) $ l) === [Just i]
findMultiparts i r === []
prop_list_multipart :: Property
prop_list_multipart =
withTests 2 . property . liftAWS $ do
(a, i) <- newMultipart
lift $ sendMultipart "" a 1 i
l <- lift $ listMultiparts (bucket a)
multipartExists i l
prop_list_parts :: Property
prop_list_parts =
withTests 2 . property . liftAWS $ do
(a, i) <- newMultipart
lift $ sendMultipart "" a 1 i
l2 <- lift $ listMultipartParts a i
length l2 === 1
multipartExists :: Monad m => Text -> [A.MultipartUpload] -> PropertyT m ()
multipartExists uploadId multiparts =
L.length (L.filter (findMultipart uploadId) multiparts) === 1
findMultiparts :: Text -> [A.MultipartUpload] -> [A.MultipartUpload]
findMultiparts uploadId =
L.filter (findMultipart uploadId)
findMultipart :: Text -> A.MultipartUpload -> Bool
findMultipart uploadId m =
m ^. A.muUploadId == Just uploadId
prop_list :: Property
prop_list =
withTests 2 . property . liftAWS $ do
m <- forAll $ Gen.text (Range.linear 10 20) Gen.alphaNum
s <- forAll $ Gen.text (Range.linear 20 30) Gen.alphaNum
a <- newAddress
lift $ Unsafe.write (withKey (// Key m) a) ""
lift $ Unsafe.write (withKey (// (Key s // Key m)) a) ""
r' <- lift $ list a
(Just . Key <$> [m, s <> "/"]) === (removeCommonPrefix a <$> r')
prop_listObjects :: Property
prop_listObjects =
withTests 2 . property . liftAWS $ do
m <- forAll $ Gen.text (Range.linear 10 20) Gen.alphaNum
s <- forAll $ Gen.text (Range.linear 20 30) Gen.alphaNum
a <- newAddress
lift $ Unsafe.write (withKey (// Key m) a) ""
lift $ Unsafe.write (withKey (// (Key s // Key m)) a) ""
(p, k) <- lift $ listObjects a
([Just . Key $ s <> "/"], [Just $ Key m]) === (removeCommonPrefix a <$> p, removeCommonPrefix a <$> k)
prop_list_recursively :: Property
prop_list_recursively =
withTests 2 . property . liftAWS $ do
a <- newAddress
lift $ Unsafe.write a ""
r' <- lift $ listRecursively (a { key = dirname $ key a })
assert $ a `elem` r'
prop_list_forbidden_bucket :: Property
prop_list_forbidden_bucket =
withTests 1 . property . liftAWS $ do
_ <- lift $ write (Address (Bucket "ambiata-dev-view") (Key "")) ""
success
prop_download :: Property
prop_download =
withTests 1 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.linear 10 20) Gen.alphaNum
l <- forAll $ genLocalPath
p <- newFilePath
a <- newAddress
let t = p </> localPath l
lift $ Unsafe.write a d
r <- lift . runExceptT $ download a t
res <- liftIO $ T.readFile t
assert $ isRight r
res === d
prop_download_multipart :: Property
prop_download_multipart =
withTests 1 . property . liftAWS $ do
c <- forAll $ Gen.text (Range.linear 10 20) Gen.alphaNum
m <- forAll $ Gen.text (Range.linear 20 30) Gen.alphaNum
bs <- forAll $ Gen.utf8 (Range.linear 1 100) Gen.unicodeAll
p <- newFilePath
a <- newAddress
let t = p </> T.unpack c
let o = p </> T.unpack m
liftIO . D.createDirectoryIfMissing True $ F.takeDirectory t
liftIO . D.createDirectoryIfMissing True $ F.takeDirectory o
liftIO $ withFile t WriteMode $ \h ->
replicateM_ 1000 (LBS.hPut h (LBS.fromChunks . return $ (BS.concat . L.replicate 10000 $ bs)))
sz <- liftIO . withFile t ReadMode $ hFileSize
lift $ Unsafe.upload t a
let ten :: Integer = 10
r <- lift . runExceptT $ multipartDownload a o (fromInteger sz) ten 100
b <- liftIO $ LBS.readFile t
let b' = sha1 b
o' <- liftIO $ LBS.readFile o
let o'' = sha1 o'
assert $ isRight r
b' === o''
prop_write_download_overwrite :: Property
prop_write_download_overwrite =
withTests 2 . property . liftAWS $ do
old <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
new <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
l <- forAll $ genLocalPath
p <- newFilePath
a <- newAddress
let t = p </> localPath l
lift $ Unsafe.write a old
x <- lift . runExceptT $ downloadWithMode Fail a t
lift $ Unsafe.writeWithMode Overwrite a new
y <- lift . runExceptT $ downloadWithMode Overwrite a t
r <- liftIO $ T.readFile t
assert $ isRight x
assert $ isRight y
r === new
prop_write_download_fail :: Property
prop_write_download_fail =
withTests 2 . property . liftAWS $ do
old <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
new <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
l <- forAll $ genLocalPath
p <- newFilePath
a <- newAddress
let t = p </> localPath l
lift $ Unsafe.write a old
x <- lift . runExceptT $ downloadWithMode Fail a t
lift $ Unsafe.writeWithMode Overwrite a new
y <- lift . runExceptT $ downloadWithMode Fail a t
assert $ isRight x
assert $ isLeft y
prop_delete :: Property
prop_delete =
withTests 2 . property . liftAWS $ do
w <- forAll Gen.genWriteMode
a <- newAddress
lift $ Unsafe.writeWithMode w a ""
x <- lift $ exists a
lift $ delete a
y <- lift $ exists a
assert x
assert $ not y
prop_delete_empty :: Property
prop_delete_empty =
withTests 2 . property . liftAWS $ do
a <- newAddress
result <- (True <$ lift (delete a)) `catchAll` (const . pure $ False)
assert result
prop_read_write :: Property
prop_read_write =
withTests 2 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
lift $ Unsafe.write a d
r <- lift $ read a
r === Just d
prop_write_failure :: Property
prop_write_failure =
withTests 2 . property . liftAWS $ do
d <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
lift $ Unsafe.write a d
r <- lift $ write a d
r === WriteDestinationExists a
prop_write_overwrite :: Property
prop_write_overwrite =
withTests 2 . property . liftAWS $ do
let
gen = Gen.text (Range.constant 5 15) Gen.alphaNum
items <- fmap toList . forAll $ Gen.set (Range.constant 2 2) gen
(x, y) <- case items of
x : y : [] ->
pure (x, y)
_ ->
annotate "Invariant generator." >> failure
a <- newAddress
lift $ Unsafe.writeWithMode Fail a x
lift $ Unsafe.writeWithMode Overwrite a y
r <- lift $ read a
r === Just y
prop_sync_overwrite :: Property
prop_sync_overwrite =
withTests 2 . property . liftAWS $ do
m <- forAll $ Gen.text (Range.linear 5 20) Gen.alphaNum
a <- newAddress
b <- newAddress
createSmallFiles a m 10
x <- lift . runExceptT $ syncWithMode OverwriteSync a b 1
y <- lift . runExceptT $ syncWithMode OverwriteSync a b 1
lift . forM_ (files b m 10) $ \e ->
exists e >>= \e' ->
when (e' == False) $
(throwM . userError $ "Output files do not exist")
assert $ isRight x
assert $ isRight y
prop_sync_fail :: Property
prop_sync_fail =
withTests 2 . property . liftAWS $ do
m <- forAll $ Gen.text (Range.linear 5 20) Gen.alphaNum
a <- newAddress
b <- newAddress
createSmallFiles a m 1
x <- lift . runExceptT $ syncWithMode FailSync a b 1
y <- lift . runExceptT $ syncWithMode FailSync a b 1
case y of
(Left (SyncError (WorkerError (OutputExists q)))) ->
q === withKey (// Key (m <> "-1")) b
_ ->
failure
assert $ isRight x
-- | If the object does not exist, then the behaviour should be invariant with the WriteMode
prop_write_nonexisting :: Property
prop_write_nonexisting =
withTests 2 . property . liftAWS $ do
w <- forAll Gen.genWriteMode
d <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
lift $ Unsafe.writeWithMode w a d
r <- lift $ read a
r === Just d
prop_write_grant :: Property
prop_write_grant =
withTests 2 . property . liftAWS $ do
t <- forAll $ Gen.text (Range.linear 0 100) Gen.alphaNum
a <- newAddress
lift $ Unsafe.write a t
lift . grantReadAccess a $ ReadGrant "id=e3abd0cceaecbd471c3eaaa47bb722bf199296c5e41c9ee4222877cc91b536fc"
r <- lift $ read a
r === Just t
prop_read_empty :: Property
prop_read_empty =
withTests 1 . property $ do
k <- forAll Gen.genKey
bucket' <- liftIO testBucket
t <- liftIO . runAWSDefaultRegion . read $ Address bucket' k
t === Nothing
prop_download_recursive :: Property
prop_download_recursive =
withTests 1 . property . liftAWS $ do
let name1 = "first name"
name2 = "second name"
name3 = "third name"
tmpdir <- newFilePath
addr <- withKey (// Key "top") <$> newAddress
lift $ do
Unsafe.write (withKey (// Key "a") addr) name1
Unsafe.write (withKey (// Key "b/c") addr) name2
Unsafe.write (withKey (// Key "c/d/e") addr) name3
lift $ either (fail . show) pure =<< runExceptT (downloadRecursive addr tmpdir)
a <- liftIO $ T.readFile (tmpdir </> "a")
c <- liftIO $ T.readFile (tmpdir </> "b" </> "c")
e <- liftIO $ T.readFile (tmpdir </> "c" </> "d" </> "e")
a === name1
c === name2
e === name3
prop_upload_recursive :: Property
prop_upload_recursive =
withTests 1 . property . liftAWS $ do
let name1 = "first name"
name2 = "second name"
name3 = "third name"
tmpdir <- newFilePath
liftIO $ do
D.createDirectoryIfMissing True (tmpdir </> "b")
D.createDirectoryIfMissing True (tmpdir </> "c" </> "d")
T.writeFile (tmpdir </> "a") name1
T.writeFile (tmpdir </> "b" </> "c") name2
T.writeFile (tmpdir </> "c" </> "d" </> "e") name3
addr <- withKey (// Key "top") <$> newAddress
lift $ either (fail . show) pure =<< runExceptT (uploadRecursive tmpdir addr 2)
a <- lift $ read (withKey (// Key "a") addr)
c <- lift $ read (withKey (// Key "b/c") addr)
e <- lift $ read (withKey (// Key "c/d/e") addr)
a === Just name1
c === Just name2
e === Just name3
prop_on_status_ok :: Property
prop_on_status_ok =
withTests 2 . property . liftAWS $ do
let
handler _ = Just 2
r <- lift $ onStatus_ (1 :: Int) handler (void (exists (Address (Bucket "ambiata-dev-view") (Key ""))))
r === 1
prop_on_status_ko :: Property
prop_on_status_ko =
withTests 2 . property . liftAWS $ do
let
handler _ = Just 2
r <- lift $ onStatus_ (1 :: Int) handler (void (write missingAddress "text"))
r === 2
----------
-- HELPERS
----------
missingAddress :: Address
missingAddress =
Address (Bucket "ambiata-missing") (Key "m")
sha1 :: LBS.ByteString -> Digest SHA1
sha1 =
hashlazy
tests :: IO Bool
tests =
checkParallel $$(discover)