amazonka-core-2.0: test/Test/Amazonka/Sign/V4/Base.hs
-- |
-- Module : Test.AWS.Sign.V4
-- Copyright : (c) 2013-2023 Brendan Hay
-- License : Mozilla Public License, v. 2.0.
-- Maintainer : Brendan Hay <brendan.g.hay+amazonka@gmail.com>
-- Stability : provisional
-- Portability : non-portable (GHC extensions)
module Test.Amazonka.Sign.V4.Base where
import Amazonka.Core
import Amazonka.Data
import Amazonka.Prelude
import Amazonka.Sign.V4
import Amazonka.Sign.V4.Base as Base
import qualified Data.Foldable as Fold
import Test.Amazonka.Arbitrary ()
import qualified Test.QuickCheck as QC
import Test.QuickCheck.Property ()
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
tests :: TestTree
tests =
testGroup
"paths"
[ testProperty "request paths are escaped once" testRequestPathsEscapedOnce,
testProperty "S3 canonical paths are escaped once" testS3CanonicalPathsEscapedOnce,
testProperty "non-S3 canonical paths are escaped twice" testNonS3CanonicalPathsEscapedTwice,
testCase "empty path" testEmptyPath,
testCase "base path" testBasePath,
testCase "S3 canonical path encoding example" testS3CanonicalPathExample,
testCase "non-S3 canonical path encoding example" testNonS3CanonicalPathExample,
testCase "S3 canonical path is not normalized" testS3ShouldNotNormalize,
testCase "non-S3 canonical path is normalized" testNonS3ShouldNormalize
]
testRequestPathsEscapedOnce :: Property
testRequestPathsEscapedOnce = QC.forAll (mkV4Paths args) $ \(raw, reqPath, _) ->
untag reqPath == toBS (escapePath raw)
where
args =
V4PathArgs
{ genAbbrev = arbitrary,
genBasePath = mkNormalizedPath,
genPath = mkNormalizedPath
}
testS3CanonicalPathsEscapedOnce :: Property
testS3CanonicalPathsEscapedOnce = QC.forAll (mkV4Paths args) $ \(_, reqPath, canonicalPath') ->
untag reqPath == untag canonicalPath'
where
args =
V4PathArgs
{ genAbbrev = pure "S3",
genBasePath = mkNormalizedPath,
genPath = mkNormalizedPath
}
testNonS3CanonicalPathsEscapedTwice :: Property
testNonS3CanonicalPathsEscapedTwice = QC.forAll (mkV4Paths args) $ \(raw, _, canonicalPath') ->
untag canonicalPath' == toBS (escapePathTwice raw)
where
args =
V4PathArgs
{ genAbbrev = mkNonS3Service,
genBasePath = mkNormalizedPath,
genPath = mkNormalizedPath
}
testEmptyPath :: Assertion
testEmptyPath = do
(_, reqPath, canonicalPath') <-
generate . mkV4Paths $
V4PathArgs
{ genAbbrev = arbitrary,
genBasePath = pure mempty,
genPath = pure mempty
}
toBS reqPath @?= "/"
toBS canonicalPath' @?= "/"
testBasePath :: Assertion
testBasePath = do
basePath <- generate arbitrary
path <- generate arbitrary
(_, reqPath, _) <-
generate . mkV4Paths $
V4PathArgs
{ genAbbrev = arbitrary,
genBasePath = pure basePath,
genPath = pure path
}
toBS reqPath @?= toBS (escapePath $ basePath <> path)
testNonS3CanonicalPathExample :: Assertion
testNonS3CanonicalPathExample = do
let example = Raw ["documents and settings"]
(_, reqPath, canonicalPath') <-
generate . mkV4Paths $
V4PathArgs
{ genAbbrev = mkNonS3Service,
genBasePath = pure mempty,
genPath = pure example
}
toBS reqPath @?= "/documents%20and%20settings"
toBS canonicalPath' @?= "/documents%2520and%2520settings"
testS3CanonicalPathExample :: Assertion
testS3CanonicalPathExample = do
let example = Raw ["documents and settings"]
(_, reqPath, canonicalPath') <-
generate . mkV4Paths $
V4PathArgs
{ genAbbrev = pure "S3",
genBasePath = pure mempty,
genPath = pure example
}
toBS reqPath @?= "/documents%20and%20settings"
untag canonicalPath' @?= untag reqPath
testS3ShouldNotNormalize :: Assertion
testS3ShouldNotNormalize = do
let key = Raw ["foo", "..", "bar", ".", "baz", "."]
(_, reqPath, canonicalPath') <-
generate . mkV4Paths $
V4PathArgs
{ genAbbrev = pure "S3",
genBasePath = pure mempty,
genPath = pure key
}
toBS reqPath @?= "/foo/../bar/./baz/."
untag canonicalPath' @?= untag reqPath
testNonS3ShouldNormalize :: Assertion
testNonS3ShouldNormalize = do
let key = Raw ["foo", "..", "bar", ".", "baz", "."]
(_, reqPath, canonicalPath') <-
generate . mkV4Paths $
V4PathArgs
{ genAbbrev = mkNonS3Service,
genBasePath = pure mempty,
genPath = pure key
}
toBS reqPath @?= "/bar/baz"
untag canonicalPath' @?= untag reqPath
data V4PathArgs = V4PathArgs
{ genAbbrev :: Gen Abbrev,
genBasePath :: Gen RawPath,
genPath :: Gen RawPath
}
mkV4Paths :: V4PathArgs -> Gen (RawPath, Base.Path, CanonicalPath)
mkV4Paths V4PathArgs {..} = do
aReq <- arbitrary :: Gen (Request ())
aBasePath <- genBasePath
aPath <- genPath
aService@Service {endpoint} <- arbitrary
anAbbrev <- genAbbrev
aBody <- toHashed <$> (arbitrary :: Gen ByteString)
let reg = NorthVirginia
end = endpoint reg
svc =
aService
{ signer = v4,
abbrev = anAbbrev,
endpoint = const $ end {basePath = aBasePath}
}
req =
aReq
{ path = aPath,
service = svc,
body = Hashed aBody
}
pure (fullRawPath reg req, escapedPath reg req, canonicalPath reg req)
mkNormalizedPath :: Gen RawPath
mkNormalizedPath = arbitrary `suchThat` noDots
where
noDots (Raw xs :: RawPath) = isNothing $ Fold.find (\x -> x == "." || x == "..") xs
mkNonS3Service :: Gen Abbrev
mkNonS3Service = arbitrary `suchThat` (/= "S3")