diff --git a/swift/example_code/swift-sdk/mocking/Package.swift b/swift/example_code/swift-sdk/mocking/Package.swift new file mode 100644 index 00000000000..1c4a760e267 --- /dev/null +++ b/swift/example_code/swift-sdk/mocking/Package.swift @@ -0,0 +1,43 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to +// build this package. +// +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import PackageDescription + +let package = Package( + name: "mocking", + dependencies: [ + // Dependencies declare other packages that this package depends on. + .package( + url: "https://github.com/awslabs/aws-sdk-swift", + from: "0.20.0" + ) + ], + // snippet-start:[mocking.swift.package.targets] + targets: [ + // A target defines a module or a test suite. A target can depend on + // other targets in this package. They can also depend on products in + // other packages that this package depends on. + .executableTarget( + name: "mocking", + dependencies: [ + .product(name: "AWSS3", package: "aws-sdk-swift"), + ], + path: "./Sources" + ), + // snippet-start:[mocking.swift.package.testTarget] + .testTarget( + name: "mocking-tests", + dependencies: [ + .product(name: "AWSS3", package: "aws-sdk-swift"), + "mocking" + ], + path: "./Tests" + ) + // snippet-end:[mocking.swift.package.testTarget] + ] + // snippet-end:[mocking.swift.package.targets] +) diff --git a/swift/example_code/swift-sdk/mocking/Sources/BucketManager.swift b/swift/example_code/swift-sdk/mocking/Sources/BucketManager.swift new file mode 100644 index 00000000000..b78afd40d81 --- /dev/null +++ b/swift/example_code/swift-sdk/mocking/Sources/BucketManager.swift @@ -0,0 +1,49 @@ +// +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// A class to manage Amazon Simple Storage Service (Amazon S3) operations +// using the ``S3Session`` class to access S3. +// + +import Foundation +import ClientRuntime +import AWSS3 + +/// A class that uses an object that uses an object of type +/// ``S3SessionProtocol`` to access Amazon S3. +// snippet-start:[mocking.swift.using-session.class] +public class BucketManager { + /// The object based on the ``S3SessionProtocol`` protocol through which to + /// call SDK for swift functions. This may be either ``S3Session`` or + /// ``MockS3Session``. + var session: S3SessionProtocol + + /// Initialize the ``S3Manager`` to call Amazon S3 functions using the + /// specified object that implements ``S3SessionProtocol``. + /// + /// - Parameter session: The session object to use when calling Amazon S3. + // snippet-start:[mocking.swift.using-session.init] + init(session: S3SessionProtocol) { + self.session = session + } + // snippet-end:[mocking.swift.using-session.init] + + /// Return an array listing all of the user's buckets by calling the + /// ``S3SessionProtocol`` function `listBuckets()`. + /// + /// - Returns: An array of bucket name strings. + /// + // snippet-start:[mocking.swift.using-session.calling] + public func getBucketNames() async throws -> [String] { + let output = try await session.listBuckets(input: ListBucketsInput()) + + guard let buckets = output.buckets else { + return [] + } + + return buckets.map { $0.name ?? "" } + } + // snippet-end:[mocking.swift.using-session.calling] +} +// snippet-end:[mocking.swift.using-session.class] diff --git a/swift/example_code/swift-sdk/mocking/Sources/S3Session.swift b/swift/example_code/swift-sdk/mocking/Sources/S3Session.swift new file mode 100644 index 00000000000..54a28b4dbc6 --- /dev/null +++ b/swift/example_code/swift-sdk/mocking/Sources/S3Session.swift @@ -0,0 +1,56 @@ +// +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// A protocol and implementation to allow calling and mocking of the AWS SDK +// for Swift's `S3Client.listBuckets(input:)` function. +// + +import Foundation +import ClientRuntime +import AWSS3 + +// snippet-start:[mocking.swift.protocol] +/// The S3SessionProtocol protocol describes the Amazon S3 functions this +/// program uses during an S3 session. It needs to be implemented once to call +/// through to the corresponding SDK for Swift functions, and a second time to +/// instead return mock results. +public protocol S3SessionProtocol { + func listBuckets(input: ListBucketsInput) async throws + -> ListBucketsOutputResponse +} +// snippet-end:[mocking.swift.protocol] + +/// An implementation of ``S3SessionProtocol`` that calls the equivalent +/// functions in the AWS SDK for Swift. This class is used by the main program +/// instead of calling the SDK directly. +// snippet-start:[mocking.swift.session] +public class S3Session: S3SessionProtocol { + let client: S3Client + let region: String + + /// Initialize the session to use the specified AWS Region. + /// + /// - Parameter region: The AWS Region to use. Default is `us-east-1`. + init(region: String = "us-east-1") throws { + self.region = region + + // Create an ``S3Client`` to use for AWS SDK for Swift calls. + self.client = try S3Client(region: self.region) + } + + /// Call through to the ``S3Client`` function `listBuckets()`. + /// + /// - Parameter input: The input to pass through to the SDK function + /// `listBuckets()`. + /// + /// - Returns: A ``ListBucketsOutputResponse`` with the returned data. + /// + // snippet-start:[mocking.swift.implement-real] + public func listBuckets(input: ListBucketsInput) async throws + -> ListBucketsOutputResponse { + return try await self.client.listBuckets(input: input) + } + // snippet-end:[mocking.swift.implement-real] +} +// snippet-end:[mocking.swift.session] diff --git a/swift/example_code/swift-sdk/mocking/Sources/mocking.swift b/swift/example_code/swift-sdk/mocking/Sources/mocking.swift new file mode 100644 index 00000000000..2674179b4ed --- /dev/null +++ b/swift/example_code/swift-sdk/mocking/Sources/mocking.swift @@ -0,0 +1,59 @@ +// +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// An example demonstrating how to mock AWS SDK for Swift functions using +// protocols. +// + +import Foundation +import ClientRuntime +import AWSS3 + +/// The main entry point for the example is an asynchronous main function. +@main +struct MockingDemo { + /// The static, asynchronous entry point for the program. + static func main() async { + // snippet-start:[mocking.swift.main-setup] + /// An ``S3Session`` object that passes calls through to the SDK for + /// Swift. + let session: S3Session + /// A ``BucketManager`` object that will be initialized to call the + /// SDK using the session. + let bucketMgr: BucketManager + + // Create the ``S3Session`` and a ``BucketManager`` that calls the SDK + // using it. + do { + session = try S3Session(region: "us-east-1") + bucketMgr = BucketManager(session: session) + } catch { + print("Unable to initialize access to Amazon S3.") + return + } + // snippet-end:[mocking.swift.main-setup] + + // snippet-start:[mocking.swift.main-call] + let bucketList: [String] + + do { + bucketList = try await bucketMgr.getBucketNames() + } catch { + print("Unable to get the bucket list.") + return + } + // snippet-end:[mocking.swift.main-call] + + // Print out a list of the bucket names. + + if bucketList.count != 0 { + print("Found \(bucketList.count) buckets:") + for name in bucketList { + print(" \(name)") + } + } else { + print("No buckets found.") + } + } +} diff --git a/swift/example_code/swift-sdk/mocking/Tests/MockS3Session.swift b/swift/example_code/swift-sdk/mocking/Tests/MockS3Session.swift new file mode 100644 index 00000000000..fc2b1ed4ceb --- /dev/null +++ b/swift/example_code/swift-sdk/mocking/Tests/MockS3Session.swift @@ -0,0 +1,95 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// An implementation of ``S3SessionProtocol`` that returns mock data instead +// of calling through to Amazon Web Services (AWS). + +import Foundation +import ClientRuntime +import AWSS3 + +@testable import mocking + +/// A structure format used to provide the data from which mock Amazon S3 +/// buckets are built. +struct MockBucketInfo { + /// The bucket's name. + var name: String + /// The bucket's creation timestamp. + var created: Date +} + +// snippet-start:[mocking.swift.mocksession] +/// The ``MockS3Session`` type implements ``S3SessionProtocol`` but instead of +/// calling through to the AWS SDK for Swift, its implementation of SDK +/// functions return mocked results. +public class MockS3Session: S3SessionProtocol { + /// An array of data used to construct the mock bucket descriptions. + private(set) var mockInfo: [MockBucketInfo] + /// An array of mock bucket descriptions as SDK `Bucket` objects. + var mockBuckets: [S3ClientTypes.Bucket] = [] + /// A date formatter to convert ISO format date strings into timestamps. + let isoDateFormatter = ISO8601DateFormatter() + + /// Initialize the mock session with some pretend buckets. + init() { + self.mockInfo = [ + MockBucketInfo( + name: "swift", + created: isoDateFormatter.date(from: "2014-06-02T11:45:00-07:00")! + ), + MockBucketInfo( + name: "amazon", + created: isoDateFormatter.date(from: "1995-07-16T08:00:00-07:00")! + ), + MockBucketInfo( + name: "moon", + created: isoDateFormatter.date(from: "1969-07-20T13:17:39-07:00")! + ) + ] + + // Construct an array of `S3ClientTypes.Bucket` objects containing the + // mock bucket data. The bucket objects only contain the minimum data + // needed to test against. Update this if additional bucket + // information is used by the main program. + + for item in self.mockInfo { + let bucket = S3ClientTypes.Bucket( + creationDate: item.created, + name: item.name + ) + self.mockBuckets.append(bucket) + } + } + + /// Compare the specified names to the mock data and see if the names match. + /// + /// - Parameter names: An array of bucket names to compare against the + /// expected names. + /// + /// - Returns: `true` if the names match. `false` if they don't. + func checkBucketNames(names: [String]) -> Bool { + let sortedMockNames = (self.mockInfo.map { $0.name }).sorted() + + return sortedMockNames == names.sorted() + } + + // snippet-start:[mocking.swift.implement-mock] + /// An implementation of the Amazon S3 function `listBuckets()` that + /// returns the mock data instead of accessing AWS. + /// + /// - Parameter input: The input to the `listBuckets()` function. + /// + /// - Returns: A `ListBucketsOutputResponse` object containing the list of + /// buckets. + public func listBuckets(input: ListBucketsInput) async throws + -> ListBucketsOutputResponse { + let response = ListBucketsOutputResponse( + buckets: self.mockBuckets, + owner: nil + ) + return response + } + // snippet-end:[mocking.swift.implement-mock] +} +// snippet-end:[mocking.swift.mocksession] diff --git a/swift/example_code/swift-sdk/mocking/Tests/mocking-tests.swift b/swift/example_code/swift-sdk/mocking/Tests/mocking-tests.swift new file mode 100644 index 00000000000..7ad392b8b3f --- /dev/null +++ b/swift/example_code/swift-sdk/mocking/Tests/mocking-tests.swift @@ -0,0 +1,49 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Tests for the AWS SDK for Swift example. This demonstrates how to mock SDK +// functions. + +import XCTest +import Foundation +import ClientRuntime +import AWSS3 + +@testable import mocking + +// snippet-start:[mocking.swift.tests] +final class MockingTests: XCTestCase { + /// The session to use for Amazon S3 calls. In this case, it's a mock + /// implementation. + var session: MockS3Session? = nil + /// The ``BucketManager`` that uses the session to perform Amazon S3 + /// operations. + var bucketMgr: BucketManager? = nil + + /// Perform one-time initialization before executing any tests. + override class func setUp() { + super.setUp() + SDKLoggingSystem.initialize(logLevel: .error) + } + + /// Set up things that need to be done just before each + /// individual test function is called. + override func setUp() { + super.setUp() + + // snippet-start:[mocking.swift.tests-setup] + self.session = MockS3Session() + self.bucketMgr = BucketManager(session: self.session!) + // snippet-end:[mocking.swift.tests-setup] + } + + // snippet-start:[mocking.swift.tests-call] + /// Test that `getBucketNames()` returns the expected results. + func testGetBucketNames() async throws { + let returnedNames = try await self.bucketMgr!.getBucketNames() + XCTAssertTrue(self.session!.checkBucketNames(names: returnedNames), + "Bucket names don't match") + } + // snippet-end:[mocking.swift.tests-call] +} +// snippet-end:[mocking.swift.tests]