Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(logging): add logging plugin integration test #3218

Merged
merged 15 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/integ_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,8 @@ jobs:
needs: prepare-for-test
uses: ./.github/workflows/integ_test_storage.yml
secrets: inherit

logging-test:
needs: prepare-for-test
uses: ./.github/workflows/integ_test_logging.yml
secrets: inherit
90 changes: 90 additions & 0 deletions .github/workflows/integ_test_logging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Integration Tests | Logging
on:
workflow_dispatch:
workflow_call:

permissions:
id-token: write
contents: read

jobs:
logging-integration-test-iOS:
runs-on: macos-12
environment: IntegrationTest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
with:
persist-credentials: false

- name: Make directory
run: mkdir -p ~/.aws-amplify/amplify-ios/testconfiguration/

- name: Copy integration test resouces
uses: ./.github/composite_actions/download_test_configuration
with:
resource_subfolder: logging
aws_role_to_assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws_region: ${{ secrets.AWS_REGION }}
aws_s3_bucket: ${{ secrets.AWS_S3_BUCKET_INTEG_V2 }}

- name: Run Integration test
uses: ./.github/composite_actions/run_xcodebuild_test
with:
project_path: ./AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginHostApp
scheme: AWSCloudWatchLoggingPluginIntegrationTests

logging-integration-test-tvOS:
runs-on: macos-13
environment: IntegrationTest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
with:
persist-credentials: false

- name: Make directory
run: mkdir -p ~/.aws-amplify/amplify-ios/testconfiguration/

- name: Copy integration test resouces
uses: ./.github/composite_actions/download_test_configuration
with:
resource_subfolder: logging
aws_role_to_assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws_region: ${{ secrets.AWS_REGION }}
aws_s3_bucket: ${{ secrets.AWS_S3_BUCKET_INTEG_V2 }}

- name: Run Integration test
uses: ./.github/composite_actions/run_xcodebuild_test
with:
project_path: ./AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginHostApp
scheme: AWSCloudWatchLoggingPluginIntegrationTests
destination: platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=latest
sdk: appletvsimulator
xcode_path: '/Applications/Xcode_14.3.app'

logging-integration-test-watchOS:
runs-on: macos-13
environment: IntegrationTest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
with:
persist-credentials: false

- name: Make directory
run: mkdir -p ~/.aws-amplify/amplify-ios/testconfiguration/

- name: Copy integration test resouces
uses: ./.github/composite_actions/download_test_configuration
with:
resource_subfolder: logging
aws_role_to_assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws_region: ${{ secrets.AWS_REGION }}
aws_s3_bucket: ${{ secrets.AWS_S3_BUCKET_INTEG_V2 }}

- name: Run Integration test
uses: ./.github/composite_actions/run_xcodebuild_test
with:
project_path: ./AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginHostApp
scheme: AWSCloudWatchLoggingPluginIntegrationTestsWatch
destination: platform=watchOS Simulator,name=Apple Watch Series 8 (45mm),OS=latest
sdk: watchsimulator
xcode_path: '/Applications/Xcode_14.3.app'
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public class AWSCloudWatchLoggingPlugin: LoggingCategoryPlugin {
localStore.reset()
}

DispatchQueue.main.async {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
self.loggingClient.takeUserIdentifierFromCurrentUser()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation
import AWSCloudWatchLogs

class AWSCloudWatchClientHelper {
static func getFilterLogEventCount(client: CloudWatchLogsClientProtocol?, filterPattern: String?, startTime: Date?, endTime: Date?, logGroupName: String?) async throws -> [CloudWatchLogsClientTypes.FilteredLogEvent]? {
let filterEventInput = FilterLogEventsInput(endTime: endTime?.epochMilliseconds, filterPattern: filterPattern, logGroupName: logGroupName, startTime: startTime?.epochMilliseconds)
let response = try await client?.filterLogEvents(input: filterEventInput)
return response?.events
}
}

extension Date {
var epochMilliseconds: Int {
Int(self.timeIntervalSince1970 * 1_000)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import XCTest
import AWSCloudWatchLogs
@testable import Amplify
@testable import AWSCognitoAuthPlugin
@testable import AWSCloudWatchLoggingPlugin

class AWSCloudWatchLoggingPluginIntergrationTests: XCTestCase {
let amplifyConfigurationFile = "testconfiguration/AWSCloudWatchLoggingPluginIntegrationTests-amplifyconfiguration"
let amplifyConfigurationLoggingFile = "testconfiguration/AWSCloudWatchLoggingPluginIntegrationTests-amplifyconfiguration_logging"
var loggingConfiguration: AWSCloudWatchLoggingPluginConfiguration?

override func setUp() async throws {
continueAfterFailure = false
do {
try Amplify.add(plugin: AWSCognitoAuthPlugin())
let loggingConfigurationFile = try TestConfigHelper.retrieveLoggingConfiguration(forResource: amplifyConfigurationLoggingFile)
loggingConfiguration = try AWSCloudWatchLoggingPluginConfiguration.loadConfiguration(from: loggingConfigurationFile)
let loggingPlugin = AWSCloudWatchLoggingPlugin(loggingPluginConfiguration: loggingConfiguration)
try Amplify.add(plugin: loggingPlugin)
let configuration = try TestConfigHelper.retrieveAmplifyConfiguration(forResource: amplifyConfigurationFile)
try Amplify.configure(configuration)
try await Task.sleep(seconds: 5)
} catch {
XCTFail("Failed to initialize and configure Amplify: \(error)")
}
XCTAssertNotNil(Amplify.Auth.plugin)
XCTAssertTrue(Amplify.Auth.isConfigured)
}

override func tearDown() async throws {
await Amplify.reset()
}

/// - Given: a AWS CloudWatch Logging plugin
/// - When: the escape hatch is requested
/// - Then: the AWS CloudWatch client is returned
func testGetEscapeHatch() throws {
let plugin = try Amplify.Logging.getPlugin(for: "awsCloudWatchLoggingPlugin")
guard let loggingPlugin = plugin as? AWSCloudWatchLoggingPlugin else {
XCTFail("Could not get plugin of type AWSCloudWatchLoggingPlugin")
return
}
let cloudWatchClient = loggingPlugin.getEscapeHatch()
XCTAssertNotNil(cloudWatchClient)
}

/// - Given: a AWS CloudWatch Logging plugin
/// - When: an error log message is logged and flushed
/// - Then: the error log message is logged and sent to AWS CloudWatch
func testFlushLogWithErrorMessage() async throws {
let category = "Analytics"
let namespace = UUID().uuidString
let message = "this is an error message in the integration test \(Date().epochMilliseconds)"
let logger = Amplify.Logging.logger(forCategory: category, forNamespace: namespace)
logger.error(message)
let plugin = try Amplify.Logging.getPlugin(for: "awsCloudWatchLoggingPlugin")
guard let loggingPlugin = plugin as? AWSCloudWatchLoggingPlugin else {
XCTFail("Could not get plugin of type AWSCloudWatchLoggingPlugin")
return
}
try await loggingPlugin.flushLogs()
try await Task.sleep(seconds: 30)
let cloudWatchClient = loggingPlugin.getEscapeHatch()
try await verifyMessageSent(client: cloudWatchClient,
logGroupName: loggingConfiguration?.logGroupName,
logLevel: "error",
message: message,
category: category,
namespace: namespace)
}

/// - Given: a AWS CloudWatch Logging plugin
/// - When: an warn log message is logged and flushed
/// - Then: the warn log message is logged and sent to AWS CloudWatch
func testFlushLogWithWarnMessage() async throws {
let category = "API"
let namespace = UUID().uuidString
let message = "this is an warn message in the integration test \(Date().epochMilliseconds)"
let logger = Amplify.Logging.logger(forCategory: category, forNamespace: namespace)
logger.warn(message)
let plugin = try Amplify.Logging.getPlugin(for: "awsCloudWatchLoggingPlugin")
guard let loggingPlugin = plugin as? AWSCloudWatchLoggingPlugin else {
XCTFail("Could not get plugin of type AWSCloudWatchLoggingPlugin")
return
}
try await loggingPlugin.flushLogs()
try await Task.sleep(seconds: 30)
let cloudWatchClient = loggingPlugin.getEscapeHatch()
try await verifyMessageSent(client: cloudWatchClient,
logGroupName: loggingConfiguration?.logGroupName,
logLevel: "warn",
message: message,
category: category,
namespace: namespace)
}

/// - Given: a AWS CloudWatch Logging plugin
/// - When: an debug log message is logged and flushed
/// - Then: the debug log message is logged and sent to AWS CloudWatch
func testFlushLogWithDebugMessage() async throws {
let category = "Geo"
let namespace = UUID().uuidString
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .long
let message = "this is an debug message in the integration test \(Date().epochMilliseconds)"
let logger = Amplify.Logging.logger(forCategory: category, forNamespace: namespace)
logger.debug(message)
let plugin = try Amplify.Logging.getPlugin(for: "awsCloudWatchLoggingPlugin")
guard let loggingPlugin = plugin as? AWSCloudWatchLoggingPlugin else {
XCTFail("Could not get plugin of type AWSCloudWatchLoggingPlugin")
return
}
try await loggingPlugin.flushLogs()
try await Task.sleep(seconds: 30)
let cloudWatchClient = loggingPlugin.getEscapeHatch()
try await verifyMessageSent(client: cloudWatchClient,
logGroupName: loggingConfiguration?.logGroupName,
logLevel: "debug",
message: message,
category: category,
namespace: namespace)
}

/// - Given: a AWS CloudWatch Logging plugin
/// - When: an info log message is logged and flushed
/// - Then: the info log message is logged and sent to AWS CloudWatch
func testFlushLogWithInfoMessage() async throws {
let category = "Auth"
let namespace = UUID().uuidString
let message = "this is an info message in the integration test \(Date().epochMilliseconds)"
let logger = Amplify.Logging.logger(forCategory: category, forNamespace: namespace)
logger.info(message)
let plugin = try Amplify.Logging.getPlugin(for: "awsCloudWatchLoggingPlugin")
guard let loggingPlugin = plugin as? AWSCloudWatchLoggingPlugin else {
XCTFail("Could not get plugin of type AWSCloudWatchLoggingPlugin")
return
}
try await loggingPlugin.flushLogs()
try await Task.sleep(seconds: 30)
let cloudWatchClient = loggingPlugin.getEscapeHatch()
try await verifyMessageSent(client: cloudWatchClient,
logGroupName: loggingConfiguration?.logGroupName,
logLevel: "info",
message: message,
category: category,
namespace: namespace)
}

/// - Given: a AWS CloudWatch Logging plugin with logging enabled
/// - When: an error log message is logged and flushed
/// - Then: the eror log message is logged and sent to AWS CloudWatch
func testFlushLogWithVerboseMessageAfterEnablingPlugin() async throws {
let category = "Storage"
let namespace = UUID().uuidString
let message = "this is an verbose message in the integration test after enabling logging \(Date().epochMilliseconds)"
let logger = Amplify.Logging.logger(forCategory: category, forNamespace: namespace)
Amplify.Logging.enable()
logger.verbose(message)
let plugin = try Amplify.Logging.getPlugin(for: "awsCloudWatchLoggingPlugin")
guard let loggingPlugin = plugin as? AWSCloudWatchLoggingPlugin else {
XCTFail("Could not get plugin of type AWSCloudWatchLoggingPlugin")
return
}
try await loggingPlugin.flushLogs()
try await Task.sleep(seconds: 30)
let cloudWatchClient = loggingPlugin.getEscapeHatch()
try await verifyMessageSent(client: cloudWatchClient,
logGroupName: loggingConfiguration?.logGroupName,
logLevel: "verbose",
message: message,
category: category,
namespace: namespace)
}

/// - Given: a AWS CloudWatch Logging plugin with logging disabled
/// - When: an error log message is logged and flushed
/// - Then: the eror log message is not logged and sent to AWS CloudWatch
func testFlushLogWithVerboseMessageAfterDisablingPlugin() async throws {
let category = "Storage"
let namespace = UUID().uuidString
let message = "this is an verbose message in the integration test after disabling logging \(Date().epochMilliseconds)"
let logger = Amplify.Logging.logger(forCategory: category, forNamespace: namespace)
Amplify.Logging.disable()
logger.verbose(message)
let plugin = try Amplify.Logging.getPlugin(for: "awsCloudWatchLoggingPlugin")
guard let loggingPlugin = plugin as? AWSCloudWatchLoggingPlugin else {
XCTFail("Could not get plugin of type AWSCloudWatchLoggingPlugin")
return
}
try await loggingPlugin.flushLogs()
try await Task.sleep(seconds: 30)
let cloudWatchClient = loggingPlugin.getEscapeHatch()
try await verifyMessageNotSent(client: cloudWatchClient,
logGroupName: loggingConfiguration?.logGroupName,
message: message)
}

func verifyMessageSent(client: CloudWatchLogsClientProtocol?,
logGroupName: String?,
logLevel: String,
message: String,
category: String,
namespace: String) async throws {

let events = try await getLastMessageSent(client: client, logGroupName: logGroupName, message: message, requestAttempt: 0)
XCTAssertEqual(events?.count, 1)
guard let sentLogMessage = events?.first?.message else {
XCTFail("Unable to verify last log message")
return
}
XCTAssertTrue(sentLogMessage.lowercased().contains(logLevel))
XCTAssertTrue(sentLogMessage.contains(message))
XCTAssertTrue(sentLogMessage.contains(category))
XCTAssertTrue(sentLogMessage.contains(namespace))
}

func verifyMessageNotSent(client: CloudWatchLogsClientProtocol?,
logGroupName: String?,
message: String) async throws {

let events = try await getLastMessageSent(client: client, logGroupName: logGroupName, message: message, requestAttempt: 0)
XCTAssertEqual(events?.count, 0)
}

func getLastMessageSent(client: CloudWatchLogsClientProtocol?,
logGroupName: String?,
message: String,
requestAttempt: Int) async throws -> [CloudWatchLogsClientTypes.FilteredLogEvent]? {
let endTime = Date()
let durationInMinutes = requestAttempt+1
let startTime = endTime.addingTimeInterval(TimeInterval(-durationInMinutes*60))
var events = try await AWSCloudWatchClientHelper.getFilterLogEventCount(client: client, filterPattern: message, startTime: startTime, endTime: endTime, logGroupName: logGroupName)

if events?.count == 0 && requestAttempt <= 5 {
try await Task.sleep(seconds: 30)
let attempted = requestAttempt + 1
events = try await getLastMessageSent(
client: client,
logGroupName: logGroupName,
message: message,
requestAttempt: attempted)
}

return events
}
}
Loading