//-----------------------------------------------------------------------------
//
// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// A copy of the License is located at
//
// http://aws.amazon.com/apache2.0
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Threading.Tasks;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Lambda;
using Amazon.Lambda.Model;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.SimpleNotificationService;
using Amazon.XRay.Recorder.Core;
using Amazon.XRay.Recorder.Core.Internal.Utils;
using Amazon.XRay.Recorder.Handlers.AwsSdk;
using Amazon.XRay.Recorder.Handlers.AwsSdk.Internal;
using Amazon.XRay.Recorder.UnitTests.Tools;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Amazon.XRay.Recorder.UnitTests
{
[TestClass]
public class AWSSDKHandlerTests : TestBase
{
private const string ManifestKey = "AwsServiceHandlerManifest";
private AWSXRayRecorder _recorder;
private static String _path = $"JSONs{Path.DirectorySeparatorChar}AWSRequestInfo.json";
#if !NETFRAMEWORK
private XRayOptions _xRayOptions = new XRayOptions();
#endif
[TestInitialize]
public void TestInitialize()
{
_recorder = new AWSXRayRecorder();
AWSXRayRecorder.InitializeInstance(recorder: _recorder);
}
[TestCleanup]
public new void TestCleanup()
{
base.TestCleanup();
#if NETFRAMEWORK
ConfigurationManager.AppSettings[ManifestKey] = null;
AppSettings.Reset();
#else
_xRayOptions.AwsServiceHandlerManifest = null;
_xRayOptions = new XRayOptions();
#endif
_recorder.Dispose();
_recorder = null;
AWSXRayRecorder.Instance.Dispose();
}
[TestMethod]
public async Task TestContextMissingStrategyForAWSSDKHandler()
{
AWSXRayRecorder.Instance.ContextMissingStrategy = Core.Strategies.ContextMissingStrategy.LOG_ERROR;
AWSSDKHandler.RegisterXRayForAllServices();
var dynamo = new AmazonDynamoDBClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
CustomResponses.SetResponse(dynamo);
AWSXRayRecorder.Instance.BeginSegment("test dynamo", TraceId);
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
AWSXRayRecorder.Instance.EndSegment();
// The test should not break. No segment is available in the context, however, since the context missing strategy is log error,
// no exception should be thrown by below code.
await dynamo.ListTablesAsync();
Assert.IsNotNull(segment);
}
[TestMethod]
public async Task TestS3SubsegmentNameIsCorrectForAWSSDKHandler()
{
AWSSDKHandler.RegisterXRay();
var s3 = new AmazonS3Client(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
CustomResponses.SetResponse(s3, null, "TestAmazonId");
_recorder.BeginSegment("test s3", TraceId);
await s3.GetObjectAsync("testBucket", "testKey");
var segment = _recorder.TraceContext.GetEntity();
_recorder.EndSegment();
Assert.AreEqual("S3", segment.Subsegments[0].Name);
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("version_id"));
Assert.AreEqual(segment.Subsegments[0].Aws["bucket_name"], "testBucket");
Assert.AreEqual(segment.Subsegments[0].Aws["operation"], "GetObject");
Assert.AreEqual(segment.Subsegments[0].Aws["id_2"], "TestAmazonId");
}
[TestMethod]
public async Task TestDynamoDbClient()
{
AWSSDKHandler.RegisterXRayForAllServices(_path);
// IAmazonDynamoDb will be registered. All new instances of AmazonServiceClient will be automatically registered.
using (var client = new AmazonDynamoDBClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1))
{
string requestId = @"fakerequ-esti-dfak-ereq-uestidfakere";
CustomResponses.SetResponse(client, requestId);
_recorder.BeginSegment("test", TraceId);
await client.ListTablesAsync();
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
var subsegment = segment.Subsegments[0];
_recorder.EndSegment();
Assert.AreEqual(segment.Subsegments.Count, 1);
Assert.AreEqual(subsegment.Name, "DynamoDBv2");
Assert.AreEqual(subsegment.Aws["region"], RegionEndpoint.USEast1.SystemName);
Assert.AreEqual(subsegment.Aws["operation"], "ListTables");
Assert.AreEqual(requestId, subsegment.Aws["request_id"]);
Assert.AreEqual("aws", subsegment.Namespace);
}
}
[TestMethod]
public void TestLoadServiceHandlerManifestWithDefaultConfigurationForAWSSDKHandler()
{
var handler = new XRayPipelineHandler(_path);
Assert.IsNotNull(handler.AWSServiceHandlerManifest);
}
[TestMethod]
public void TestLoadServiceHandlerManifestWithDefaultConfigurationForAWSSDKHandlerNullStream()
{
Stream stream = null;
var handler = new XRayPipelineHandler(stream);
Assert.IsNotNull(handler.AWSServiceHandlerManifest);
}
[TestMethod]
public void TestLoadServiceHandlerManifestWithDefaultConfigurationForAWSSDKHandlerAsStream()
{
using (Stream stream = new FileStream(_path, FileMode.Open, FileAccess.Read))
{
var handler = new XRayPipelineHandler(stream);
Assert.IsNotNull(handler.AWSServiceHandlerManifest);
}
}
[TestMethod]
[ExpectedException(typeof(FileNotFoundException))]
public void TestLoadServiceInfoManifestInvalidPathForAWSSDKHandler()
{
_ = new XRayPipelineHandler(@"IncorrectPath.abc");
}
[TestMethod]
public async Task TestRequestResponseParameterAndDescriptorForAWSSDKHandler()
{
using (var client = new AmazonDynamoDBClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1))
{
CustomResponses.SetResponse(client);
_recorder.BeginSegment("test", TraceId);
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
var key1 = new Dictionary() { { "id", new AttributeValue("1") } };
var key2 = new Dictionary() { { "id", new AttributeValue("2") } };
var keys = new KeysAndAttributes() { Keys = new List>() { key1, key2 } };
await client.BatchGetItemAsync(new Dictionary() { { "test", keys } });
_recorder.EndSegment();
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("request_items"));
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("responses"));
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("item_count"));
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("table_names"));
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("operation"));
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("request_id"));
}
}
[TestMethod]
public async Task TestExceptionHandlerAsyncForAWSSDKHandler()
{
using (var client = new AmazonDynamoDBClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1))
{
string requestId = @"fakerequ-esti-dfak-ereq-uestidfakere";
AmazonServiceException amazonServiceException = new AmazonServiceException();
amazonServiceException.StatusCode = System.Net.HttpStatusCode.NotFound;
amazonServiceException.RequestId = requestId;
CustomResponses.SetResponse(client, (request) => { throw amazonServiceException; });
_recorder.BeginSegment("test", TraceId);
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
try
{
await client.GetItemAsync(
"test", new Dictionary() { { "invalid_key", new AttributeValue("1") } });
Assert.Fail();
}
catch (AmazonServiceException e)
{
Assert.ReferenceEquals(e, segment.Subsegments[0].Cause.ExceptionDescriptors[0].Exception);
Assert.IsTrue(segment.Subsegments[0].Cause.ExceptionDescriptors[0].Remote); // the exception is remote
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("table_name"));
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("consistent_read"));
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("projection_expression"));
Assert.IsTrue(segment.Subsegments[0].Aws.ContainsKey("attribute_names_substituted"));
var responseInfo = segment.Subsegments[0].Http["response"] as Dictionary;
Assert.AreEqual(404, responseInfo["status"]);
var subsegment = segment.Subsegments[0];
Assert.AreEqual(requestId, subsegment.Aws["request_id"]);
Assert.IsTrue(subsegment.HasError);
Assert.IsFalse(subsegment.HasFault);
}
finally
{
_recorder.EndSegment();
}
}
}
[TestMethod]
public async Task TestDynamoSubsegmentNameIsCorrectForAWSSDKHandler()
{
var dynamo = new AmazonDynamoDBClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
CustomResponses.SetResponse(dynamo);
_recorder.BeginSegment("test dynamo", TraceId);
await dynamo.ListTablesAsync();
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
_recorder.EndSegment();
Assert.AreEqual("DynamoDBv2", segment.Subsegments[0].Name);
}
[TestMethod]
public async Task TestManifestFileNoLambda() //At this point, current manifest file doen't contain Lambda service.
{
var lambda = new AmazonLambdaClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
CustomResponses.SetResponse(lambda);
_recorder.BeginSegment("lambda", TraceId);
await lambda.InvokeAsync(new InvokeRequest
{
FunctionName = "testFunction"
});
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
_recorder.EndSegment();
Assert.IsFalse(segment.Subsegments[0].Aws.ContainsKey("function_name"));
}
[TestMethod]
public async Task TestLambdaInvokeSubsegmentContainsFunctionNameForAWSSDKHandler()
{
String temp_path = $"JSONs{Path.DirectorySeparatorChar}AWSRequestInfoWithLambda.json"; //registering manifest file with Lambda
AWSSDKHandler.RegisterXRayManifest(temp_path);
var lambda = new AmazonLambdaClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
CustomResponses.SetResponse(lambda);
_recorder.BeginSegment("lambda", TraceId);
await lambda.InvokeAsync(new InvokeRequest
{
FunctionName = "testFunction"
});
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
_recorder.EndSegment();
Assert.AreEqual("Invoke", segment.Subsegments[0].Aws["operation"]);
Assert.AreEqual("testFunction", segment.Subsegments[0].Aws["function_name"]);
}
[TestMethod]
public async Task TestRegisterXRayManifestWithStreamLambdaForAWSSDKHandler()
{
String temp_path = $"JSONs{Path.DirectorySeparatorChar}AWSRequestInfoWithLambda.json"; //registering manifest file with Lambda
using (Stream stream = new FileStream(temp_path, FileMode.Open, FileAccess.Read))
{
AWSSDKHandler.RegisterXRayManifest(stream);
}
var lambda = new AmazonLambdaClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
CustomResponses.SetResponse(lambda);
_recorder.BeginSegment("lambda", TraceId);
await lambda.InvokeAsync(new InvokeRequest
{
FunctionName = "testFunction"
});
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
_recorder.EndSegment();
Assert.AreEqual("Invoke", segment.Subsegments[0].Aws["operation"]);
}
[TestMethod]
public void TestSNSSubsegment()
{
AWSSDKHandler.RegisterXRay();
var sns = new AmazonSimpleNotificationServiceClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast1);
CustomResponses.SetResponse(sns);
_recorder.BeginSegment("test sns", TraceId);
try
{
sns.ListTopicsAsync().Wait();
}
catch
{
// will throw exception for using anonymous AWS credentials, but will not affect generating traces
}
var segment = _recorder.TraceContext.GetEntity();
_recorder.EndSegment();
Assert.AreEqual(1, segment.Subsegments.Count);
Assert.AreEqual("SNS", segment.Subsegments[0].Name); // Name should be SNS instead of SimpleNotificationService
Assert.AreEqual("ListTopics", segment.Subsegments[0].Aws["operation"]);
Assert.AreEqual("us-east-1", segment.Subsegments[0].Aws["region"]);
Assert.AreEqual("aws", segment.Subsegments[0].Namespace);
}
}
}