//----------------------------------------------------------------------------- // // 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.Collections.Generic; using System.Configuration; using Amazon.XRay.Recorder.Core; using Amazon.XRay.Recorder.Core.Internal.Entities; using Amazon.XRay.Recorder.Core.Internal.Utils; using Amazon.XRay.Recorder.Core.Plugins; using Amazon.XRay.Recorder.Core.Sampling; using Amazon.XRay.Recorder.Core.Strategies; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; using Amazon.XRay.Recorder.Core.Internal.Emitters; using System; using Amazon.XRay.Recorder.Core.Internal.Context; using Amazon.XRay.Recorder.Core.Exceptions; using Amazon.Runtime; #if !NETFRAMEWORK using Microsoft.Extensions.Configuration; #endif namespace Amazon.XRay.Recorder.UnitTests { [TestClass] public class AwsXrayRecorderBuilderTests : TestBase { private const string PluginKey = "AWSXRayPlugins"; private const string UseRuntimeErrors = "UseRuntimeErrors"; private AWSXRayRecorder _recorder; #if !NETFRAMEWORK private XRayOptions _xRayOptions = new XRayOptions(); #endif [TestInitialize] public void Initialize() { _recorder = new AWSXRayRecorder(); } [TestCleanup] public new void TestCleanup() { base.TestCleanup(); #if NETFRAMEWORK ConfigurationManager.AppSettings[PluginKey] = null; ConfigurationManager.AppSettings[UseRuntimeErrors] = null; AppSettings.Reset(); #else _xRayOptions = new XRayOptions(); #endif _recorder.Dispose(); AWSXRayRecorder.Instance.Dispose(); _recorder = null; } [TestMethod] public void TestBuildWithDummyPlugin() { var dummyPlugin = new DummyPlugin(); AWSXRayRecorder recorder = new AWSXRayRecorderBuilder().WithPlugin(dummyPlugin).Build(); recorder.BeginSegment("test", TraceId); var segment = (Segment)AWSXRayRecorder.Instance.TraceContext.GetEntity(); recorder.EndSegment(); Assert.AreEqual("Origin", segment.Origin); var dict = segment.Aws[dummyPlugin.ServiceName] as Dictionary; Assert.AreEqual("value1", dict["key1"]); } [TestMethod] public void TestWithDefaultPlugins() { #if NETFRAMEWORK ConfigurationManager.AppSettings[PluginKey] = "EC2Plugin"; AppSettings.Reset(); AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithPluginsFromAppSettings(); #else _xRayOptions.PluginSetting = "EC2Plugin"; AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithPluginsFromConfig(_xRayOptions); #endif Assert.AreEqual(1, builder.Plugins.Count); var expectedType = typeof(EC2Plugin); var actualType = builder.Plugins[0].GetType(); Assert.AreEqual(expectedType, actualType); } [TestMethod] public void TestPluginSettingMissing() { #if NETFRAMEWORK var builder = new AWSXRayRecorderBuilder().WithPluginsFromAppSettings(); #else var builder = new AWSXRayRecorderBuilder().WithPluginsFromConfig(_xRayOptions); #endif Assert.AreEqual(0, builder.Plugins.Count); } [TestMethod] public void TestInvalidPluginSetting() { #if NETFRAMEWORK ConfigurationManager.AppSettings[PluginKey] = "UnknownPlugin, IPlugin"; AppSettings.Reset(); AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithPluginsFromAppSettings(); #else _xRayOptions.PluginSetting = "UnknownPlugin, IPlugin"; AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithPluginsFromConfig(_xRayOptions); #endif Assert.AreEqual(0, builder.Plugins.Count); } [TestMethod] public void TestSetSamplingStrategy() { var recorder = new AWSXRayRecorderBuilder().WithSamplingStrategy(new DummySamplingStrategy()).Build(); Assert.AreEqual(typeof(DummySamplingStrategy).FullName, recorder.SamplingStrategy.GetType().FullName); } [TestMethod] public void TestSetStreamingStrategy() { var recorder = new AWSXRayRecorderBuilder().WithStreamingStrategy(new DummyStreamingStrategy()).Build(); Assert.AreEqual(typeof(DummyStreamingStrategy).FullName, recorder.StreamingStrategy.GetType().FullName); } [TestMethod] public void TestDefaultValueOfContextMissingStrategy() { var recorder = new AWSXRayRecorderBuilder().Build(); Assert.AreEqual(ContextMissingStrategy.LOG_ERROR, recorder.ContextMissingStrategy); } [TestMethod] public void TestSetContextMissingStrategy() { var recorder = new AWSXRayRecorderBuilder().WithContextMissingStrategy(ContextMissingStrategy.LOG_ERROR).Build(); Assert.AreEqual(ContextMissingStrategy.LOG_ERROR, recorder.ContextMissingStrategy); } [TestMethod] public void TestSetContextMissingUsingConfiguration1() // Contextmissing startegy set to log error from configuration { #if NETFRAMEWORK ConfigurationManager.AppSettings[UseRuntimeErrors] = "false"; AppSettings.Reset(); AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithContextMissingStrategyFromAppSettings(); #else _xRayOptions.UseRuntimeErrors = false; AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithContextMissingStrategyFromConfig(_xRayOptions); #endif AWSXRayRecorder recorder = builder.Build(); Assert.AreEqual(ContextMissingStrategy.LOG_ERROR, recorder.ContextMissingStrategy); } [TestMethod] public void TestSetContextMissingUsingConfiguration2() // Contextmissing strategy not set { #if NETFRAMEWORK AppSettings.Reset(); AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithContextMissingStrategyFromAppSettings(); #else AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithContextMissingStrategyFromConfig(_xRayOptions); #endif AWSXRayRecorder recorder = builder.Build(); Assert.AreEqual(ContextMissingStrategy.LOG_ERROR, recorder.ContextMissingStrategy); // Default context missing strategy is set } [TestMethod] public void TestSetContextMissingUsingConfiguration3() // Contextmissing startegy is set through environment and configurations { Environment.SetEnvironmentVariable(AWSXRayRecorder.EnvironmentVariableContextMissingStrategy, "LOG_ERROR"); #if NETFRAMEWORK ConfigurationManager.AppSettings[UseRuntimeErrors] = "true"; AppSettings.Reset(); AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithContextMissingStrategyFromAppSettings(); #else _xRayOptions.UseRuntimeErrors = true; AWSXRayRecorderBuilder builder = new AWSXRayRecorderBuilder().WithContextMissingStrategyFromConfig(_xRayOptions); #endif AWSXRayRecorder recorder = builder.Build(); Assert.AreEqual(ContextMissingStrategy.LOG_ERROR, recorder.ContextMissingStrategy); // Preference given to environment variable Environment.SetEnvironmentVariable(AWSXRayRecorder.EnvironmentVariableContextMissingStrategy, null); } [TestMethod] public void TestSetEmitter() { var recorder = new AWSXRayRecorderBuilder().WithContextMissingStrategy(ContextMissingStrategy.LOG_ERROR).WithSegmentEmitter(new DummyEmitter()).Build(); Assert.AreEqual(typeof(DummyEmitter).FullName, recorder.Emitter.GetType().FullName); } [TestMethod] public void TestSetDefaultEmitter() { var recorder = new AWSXRayRecorderBuilder().WithContextMissingStrategy(ContextMissingStrategy.LOG_ERROR).Build(); // set default UDP emitter Assert.AreEqual(typeof(UdpSegmentEmitter).FullName, recorder.Emitter.GetType().FullName); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void TestSetNullEmitter() { _ = new AWSXRayRecorderBuilder().WithContextMissingStrategy(ContextMissingStrategy.LOG_ERROR).WithSegmentEmitter(null).Build(); Assert.Fail(); } [TestMethod] public void TestSetTraceContext() { var recorder = new AWSXRayRecorderBuilder().WithTraceContext(new DummyTraceContext()).Build(); // set custom trace context Assert.AreEqual(typeof(DummyTraceContext).FullName, recorder.TraceContext.GetType().FullName); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void TestSetNullTraceContext() { _ = new AWSXRayRecorderBuilder().WithTraceContext(null).Build(); Assert.Fail(); } [TestMethod] public void TestExceptionStrategy1() // valid input { var exceptionStrategy = new DefaultExceptionSerializationStrategy(10); var recorder = new AWSXRayRecorderBuilder().WithExceptionSerializationStrategy(exceptionStrategy).Build(); // set custom stackframe size AWSXRayRecorder.InitializeInstance(recorder: recorder); Assert.AreSame(exceptionStrategy, AWSXRayRecorder.Instance.ExceptionSerializationStrategy); } [TestMethod] public void TestExceptionStrategy2() // invalid input { var exceptionStrategy = new DefaultExceptionSerializationStrategy(-10); var recorder = new AWSXRayRecorderBuilder().WithExceptionSerializationStrategy(exceptionStrategy).Build(); // set custom stackframe size AWSXRayRecorder.InitializeInstance(recorder: recorder); DefaultExceptionSerializationStrategy actual = AWSXRayRecorder.Instance.ExceptionSerializationStrategy as DefaultExceptionSerializationStrategy; Assert.AreEqual(DefaultExceptionSerializationStrategy.DefaultStackFrameSize, actual.MaxStackFrameSize); } [TestMethod] public void TestExceptionStrategy3() // Test default recorder instance { DefaultExceptionSerializationStrategy actual = AWSXRayRecorder.Instance.ExceptionSerializationStrategy as DefaultExceptionSerializationStrategy; Assert.AreEqual(DefaultExceptionSerializationStrategy.DefaultStackFrameSize, actual.MaxStackFrameSize); } [TestMethod] public void TestExceptionStrategy4() // Test custom exception strategy { var exceptionStrategy = new DummyExceptionSerializationStrategy(); var recorder = new AWSXRayRecorderBuilder().WithExceptionSerializationStrategy(exceptionStrategy).Build(); // set custom stackframe size DummyExceptionSerializationStrategy actual = recorder.ExceptionSerializationStrategy as DummyExceptionSerializationStrategy; Assert.AreSame(exceptionStrategy, actual); } [TestMethod] public void TestExceptionStrategy5() // Test custom exception strategy { List l = new List(); l.Add(typeof(ArgumentNullException)); var recorder = new AWSXRayRecorderBuilder().WithExceptionSerializationStrategy(new DefaultExceptionSerializationStrategy(10,l)).Build(); // set custom stackframe size AWSXRayRecorder.InitializeInstance(recorder: recorder); AWSXRayRecorder.Instance.BeginSegment("parent", TraceId); var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity(); try { recorder.BeginSubsegment("child1"); try { try { recorder.BeginSubsegment("child2"); throw new AmazonServiceException(); } catch (AmazonServiceException e) { recorder.AddException(e); recorder.EndSubsegment(); throw new ArgumentNullException("value"); } } catch (ArgumentNullException e) { recorder.AddException(e); recorder.EndSubsegment(); throw new EntityNotAvailableException("Dummy message", e); } } catch (EntityNotAvailableException e) { recorder.AddException(e); recorder.EndSegment(); } Assert.AreEqual("Dummy message", segment.Cause.ExceptionDescriptors[0].Message); Assert.AreEqual("EntityNotAvailableException", segment.Cause.ExceptionDescriptors[0].Type); Assert.IsFalse(segment.Cause.ExceptionDescriptors[0].Remote); // default false Assert.AreEqual(segment.Cause.ExceptionDescriptors[0].Cause, segment.Subsegments[0].Cause.ExceptionDescriptors[0].Id); Assert.AreEqual(1, segment.Cause.ExceptionDescriptors.Count); Assert.AreEqual("ArgumentNullException", segment.Subsegments[0].Cause.ExceptionDescriptors[0].Type); Assert.IsTrue(segment.Subsegments[0].Cause.ExceptionDescriptors[0].Remote); // set to true Assert.AreEqual("AmazonServiceException", segment.Subsegments[0].Subsegments[0].Cause.ExceptionDescriptors[0].Type); Assert.IsTrue(segment.Subsegments[0].Subsegments[0].Cause.ExceptionDescriptors[0].Remote); // set to true } [TestMethod] public void TestExceptionStrategy6() // Setting stack frame size to 0, so no stack trace is recorded { int stackFrameSize = 0; var recorder = new AWSXRayRecorderBuilder().WithExceptionSerializationStrategy(new DefaultExceptionSerializationStrategy(stackFrameSize)).Build(); // set custom stackframe size AWSXRayRecorder.InitializeInstance(recorder: recorder); AWSXRayRecorder.Instance.BeginSegment("parent", TraceId); var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity(); try { recorder.BeginSubsegment("child1"); try { try { recorder.BeginSubsegment("child2"); throw new AmazonServiceException(); } catch (AmazonServiceException e) { recorder.AddException(e); recorder.EndSubsegment(); throw new ArgumentNullException("value"); } } catch (ArgumentNullException e) { recorder.AddException(e); recorder.EndSubsegment(); throw new EntityNotAvailableException("Dummy message", e); } } catch (EntityNotAvailableException e) { recorder.AddException(e); recorder.EndSegment(); } Assert.AreEqual("Dummy message", segment.Cause.ExceptionDescriptors[0].Message); Assert.AreEqual("EntityNotAvailableException", segment.Cause.ExceptionDescriptors[0].Type); Assert.IsFalse(segment.Cause.ExceptionDescriptors[0].Remote); // default false Assert.AreEqual(segment.Cause.ExceptionDescriptors[0].Cause, segment.Subsegments[0].Cause.ExceptionDescriptors[0].Id); Assert.AreEqual(segment.Cause.ExceptionDescriptors[0].Stack.Length, stackFrameSize); // no stack frames should be recorded Assert.IsTrue(segment.Cause.ExceptionDescriptors[0].Truncated > 0); Assert.AreEqual(1, segment.Cause.ExceptionDescriptors.Count); Assert.AreEqual("ArgumentNullException", segment.Subsegments[0].Cause.ExceptionDescriptors[0].Type); Assert.AreEqual("AmazonServiceException", segment.Subsegments[0].Subsegments[0].Cause.ExceptionDescriptors[0].Type); } [TestMethod] public void TestExceptionStrategy7() // Test adding the same exception in different subsegments { List l = new List(); l.Add(typeof(ArgumentNullException)); var recorder = new AWSXRayRecorderBuilder().WithExceptionSerializationStrategy(new DefaultExceptionSerializationStrategy(10, l)).Build(); // set custom stackframe size AWSXRayRecorder.InitializeInstance(recorder: recorder); AWSXRayRecorder.Instance.BeginSegment("parent", TraceId); var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity(); try { recorder.BeginSubsegment("child1"); try { try { recorder.BeginSubsegment("child2"); throw new AmazonServiceException(); } catch (AmazonServiceException e) { recorder.AddException(e); recorder.EndSubsegment(); throw; } } catch (AmazonServiceException e) { recorder.AddException(e); recorder.EndSubsegment(); throw new EntityNotAvailableException("Dummy message", e); } } catch (EntityNotAvailableException e) { recorder.AddException(e); recorder.EndSegment(); } Assert.AreEqual("Dummy message", segment.Cause.ExceptionDescriptors[0].Message); Assert.AreEqual("EntityNotAvailableException", segment.Cause.ExceptionDescriptors[0].Type); Assert.IsFalse(segment.Cause.ExceptionDescriptors[0].Remote); // default false Assert.AreEqual(segment.Cause.ExceptionDescriptors[0].Cause, segment.Subsegments[0].Cause.ExceptionDescriptors[0].Id); Assert.AreEqual(1, segment.Cause.ExceptionDescriptors.Count); Assert.IsNull(segment.Subsegments[0].Cause.ExceptionDescriptors[0].Type); Assert.IsFalse(segment.Subsegments[0].Cause.ExceptionDescriptors[0].Remote); // default false Assert.AreEqual("AmazonServiceException", segment.Subsegments[0].Subsegments[0].Cause.ExceptionDescriptors[0].Type); Assert.IsTrue(segment.Subsegments[0].Subsegments[0].Cause.ExceptionDescriptors[0].Remote); // set to true } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void TestSetNullExceptionSerializationStrategy() { _ = new AWSXRayRecorderBuilder().WithExceptionSerializationStrategy(null).Build(); Assert.Fail(); } private class DummySamplingStrategy : ISamplingStrategy { public SamplingResponse ShouldTrace(SamplingInput input) { throw new NotImplementedException(); } } private class DummyStreamingStrategy : IStreamingStrategy { public bool ShouldStream(Entity entity) { throw new NotImplementedException(); } public void Stream(Entity entity, ISegmentEmitter emitter) { throw new NotImplementedException(); } } private class DummyPlugin : IPlugin { public string Origin { get { return "Origin"; } } public string ServiceName { get { return "DummyService"; } } public bool TryGetRuntimeContext(out IDictionary context) { context = new Dictionary(); context.Add("key1", "value1"); return true; } } private class DummyEmitter : ISegmentEmitter { public void Dispose() { } public void Send(Entity segment) { } public void SetDaemonAddress(string daemonAddress) { } } public class DummyTraceContext : ITraceContext { public void ClearEntity() { } public Entity GetEntity() { throw new NotImplementedException(); } public void HandleEntityMissing(IAWSXRayRecorder recorder, Exception e, string message) { throw new NotImplementedException(); } public bool IsEntityPresent() { throw new NotImplementedException(); } public void SetEntity(Entity entity) { throw new NotImplementedException(); } } private class DummyExceptionSerializationStrategy : ExceptionSerializationStrategy { public List DescribeException(Exception e, IEnumerable subsegments) { throw new NotImplementedException(); } } } }