Skip to content

Commit

Permalink
test(logging): add logging plugin integration test (#3218)
Browse files Browse the repository at this point in the history
* test: add logging pluging integration test

* test(logging): add logging integration tests

* test(logging): add logging integ test

* test(logging): add more test cases

* chore(logging): clean up build configuration

* test: fix integration test in command line

* chore: update integration test configuration

* test(logging): remove logging integration test from integration test suite

* test integration on branch

* fix integration test names

* introduce delay in getting current user id

* resolve issue with parallel integration tests running on multiple platforms

* revert base integration workflow to include all integration tests
  • Loading branch information
phantumcode authored Sep 26, 2023
1 parent 0432e8e commit 0a37e4a
Show file tree
Hide file tree
Showing 18 changed files with 1,770 additions and 1 deletion.
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

0 comments on commit 0a37e4a

Please sign in to comment.