//-----------------------------------------------------------------------------
//
// 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 Amazon.XRay.Recorder.Core;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Amazon.XRay.Recorder.Core.Internal.Entities;
using Amazon.XRay.Recorder.Core.Exceptions;
using Amazon.XRay.Recorder.Core.Sampling;
using Amazon.XRay.Recorder.Core.Internal.Context;
using Amazon.XRay.Recorder.Core.Internal.Utils;
using System.Net;
using Amazon.XRay.Recorder.Handlers.System.Net;
using Moq;
using Amazon.XRay.Recorder.Core.Internal.Emitters;
namespace Amazon.XRay.Recorder.UnitTests
{
[TestClass]
public class TestLambdaContext : TestBase
{
private const string MOCK_URL = "https://httpbin.org/";
private static readonly String _traceHeaderValue = "Root=" + TraceId + ";Parent=53995c3f42cd8ad8;Sampled=1";
private AWSXRayRecorder _recorder;
[TestInitialize]
public void Initialize()
{
Environment.SetEnvironmentVariable(AWSXRayRecorder.LambdaTaskRootKey, "test");
Environment.SetEnvironmentVariable(AWSXRayRecorder.LambdaTraceHeaderKey, _traceHeaderValue);
_recorder = new AWSXRayRecorderBuilder().Build();
AWSXRayRecorder.InitializeInstance(recorder: _recorder);
}
[TestCleanup]
public new void TestCleanup()
{
base.TestCleanup();
Environment.SetEnvironmentVariable(AWSXRayRecorder.LambdaTaskRootKey, null);
Environment.SetEnvironmentVariable(AWSXRayRecorder.LambdaTraceHeaderKey, null);
}
[TestMethod]
public void TestLamdaSampled()
{
var mockEmitter = new Mock();
using (AWSXRayRecorder recorder = AWSXRayRecorderFactory.CreateAWSXRayRecorder(mockEmitter.Object))
{
AWSXRayRecorder.InitializeInstance(recorder: _recorder);
TestLambdaHelper(recorder, true);
mockEmitter.Verify(x => x.Send(It.IsAny()), Times.Exactly(2));
TestLambdaHelper(recorder, false);
mockEmitter.Verify(x => x.Send(It.IsAny()), Times.Exactly(2));
TestLambdaHelper(recorder, true);
mockEmitter.Verify(x => x.Send(It.IsAny()), Times.Exactly(4));
TestLambdaHelper(recorder, false);
mockEmitter.Verify(x => x.Send(It.IsAny()), Times.Exactly(4));
}
}
private static void TestLambdaHelper(AWSXRayRecorder recorder, bool sampled)
{
if (sampled)
{
recorder.BeginSubsegment("subsegment1");
}
else
{
recorder.BeginSubsegmentWithoutSampling("subsegment1");
}
var request = (HttpWebRequest)WebRequest.Create(MOCK_URL);
using (var response = request.GetResponseTraced() as HttpWebResponse)
{
Assert.IsNotNull(response);
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
string header = request.Headers.Get(TraceHeader.HeaderKey);
Assert.AreEqual(sampled ? SampleDecision.Sampled : SampleDecision.NotSampled, TraceHeader.FromString(header).Sampled);
}
recorder.EndSubsegment();
}
[TestMethod]
public void TestSubsegment()
{
_recorder.BeginSubsegment("subsegment1");
Subsegment subsegment1 = (Subsegment)AWSXRayRecorder.Instance.TraceContext.GetEntity();
FacadeSegment facadeSegment = (FacadeSegment)subsegment1.RootSegment;
_recorder.EndSubsegment();
Assert.AreEqual(typeof(LambdaContextContainer), AWSXRayRecorder.Instance.TraceContext.GetType());
Assert.AreEqual(facadeSegment.GetType(), typeof(FacadeSegment));
Assert.IsFalse(facadeSegment.Subsegments.Contains(subsegment1)); // only subsegment is streamed
Assert.IsFalse(AWSXRayRecorder.Instance.TraceContext.IsEntityPresent()); // facade segment is cleared from AWSXRayRecorder.Instance.TraceContext
}
[TestMethod]
public void TestGetEntity()
{
var entity = _recorder.TraceContext.GetEntity();
Assert.AreEqual(entity.GetType(), typeof(FacadeSegment));
Assert.AreNotEqual(entity.GetType(), typeof(Subsegment));
Assert.AreNotEqual(entity.GetType(), typeof(Segment));
_recorder.BeginSubsegment("subsegment1");
Subsegment subsegment1 = (Subsegment)AWSXRayRecorder.Instance.TraceContext.GetEntity();
Entity facadeSegment = subsegment1.RootSegment;
_recorder.EndSubsegment();
Assert.AreEqual(facadeSegment.GetType(), typeof(FacadeSegment));
Assert.IsFalse(facadeSegment.Subsegments.Contains(subsegment1)); // only subsegment is streamed
Assert.IsFalse(AWSXRayRecorder.Instance.TraceContext.IsEntityPresent()); // facade segment is cleared from AWSXRayRecorder.Instance.TraceContext
Assert.AreEqual(typeof(LambdaContextContainer), AWSXRayRecorder.Instance.TraceContext.GetType());
}
[TestMethod]
public void TestNestedSubsegments()
{
_recorder.BeginSubsegment("subsegment1");
Subsegment child = (Subsegment)AWSXRayRecorder.Instance.TraceContext.GetEntity();
FacadeSegment facadeSegment = (FacadeSegment)child.RootSegment;
_recorder.BeginSubsegment("subsegment2");
Assert.AreEqual("subsegment2", AWSXRayRecorder.Instance.TraceContext.GetEntity().Name);
Assert.AreEqual(facadeSegment.TraceId, AWSXRayRecorder.Instance.TraceContext.GetEntity().RootSegment.TraceId); // root segment of subsegment2 is facade segment
_recorder.EndSubsegment();
_recorder.EndSubsegment();
Assert.AreEqual(facadeSegment.GetType(), typeof(FacadeSegment));
Assert.IsFalse(facadeSegment.Subsegments.Contains(child)); // only subsegments are streamed
Assert.IsFalse(AWSXRayRecorder.Instance.TraceContext.IsEntityPresent());
}
[TestMethod]
public void TestLambdaVariablesNotSetCorrectly()
{
String invalidTraceHeader = "Root=" + TraceId + ";Parent=53995c3f42cd8ad8"; // sample decision is missing
Environment.SetEnvironmentVariable(AWSXRayRecorder.LambdaTraceHeaderKey, invalidTraceHeader);
_recorder.BeginSubsegment("subsegment1");
Subsegment subsegment = (Subsegment)AWSXRayRecorder.Instance.TraceContext.GetEntity(); // subsegment added with sample decision set to not sampled
Assert.AreEqual(SampleDecision.NotSampled, subsegment.Sampled);
_recorder.EndSubsegment(); // subsegment not sampled since invalid TraceHeader value set in the lambda environment
Assert.IsFalse(AWSXRayRecorder.Instance.TraceContext.IsEntityPresent()); // Facade segment not present in the callcontext
}
[TestMethod]
public void TestLambdaLeakedSubsegments()
{
String secondTraceHeader = "Root=" + Core.Internal.Entities.TraceId.NewId() + ";Parent=53995c3f42cd8ad1;Sampled=1";
_recorder.BeginSubsegment("subsegment1");
Subsegment subsegment1 = (Subsegment)AWSXRayRecorder.Instance.TraceContext.GetEntity();
Environment.SetEnvironmentVariable(AWSXRayRecorder.LambdaTraceHeaderKey, secondTraceHeader);
_recorder.BeginSubsegment("subsegment2"); // Environment variables changed, subsegment1 will be dropped
Subsegment subsegment2 = (Subsegment)AWSXRayRecorder.Instance.TraceContext.GetEntity();
FacadeSegment facadeSegment = (FacadeSegment)subsegment2.RootSegment;
Assert.IsFalse(facadeSegment.Subsegments.Contains(subsegment1)); // subsegment1 dropped
Assert.IsTrue(facadeSegment.Subsegments.Contains(subsegment2)); // only subsegment2 is present
_recorder.EndSubsegment(); // subsegment2 streamed
Assert.IsFalse(AWSXRayRecorder.Instance.TraceContext.IsEntityPresent()); // Facade segment not present in the callcontext
}
[TestMethod]
public void TestNoNewSegmentInLambda()
{
try
{
_recorder.BeginSegment("test");
Assert.Fail();
}
catch (UnsupportedOperationException)
{
// expected
}
Assert.IsFalse(AWSXRayRecorder.Instance.TraceContext.IsEntityPresent()); // Segment cannot be added in Lambda Context
}
[TestMethod]
public void TestSegmentEndInLambda()
{
try
{
_recorder.EndSegment();
Assert.Fail();
}
catch (UnsupportedOperationException)
{
// expected
}
}
[TestMethod]
public void TestNotSampledNestedSubsegments()
{
String notSampledTraceHeader = "Root=" + Core.Internal.Entities.TraceId.NewId() + ";Parent=53995c3f42cd8ad1;Sampled=0"; //not sampled
Environment.SetEnvironmentVariable(AWSXRayRecorder.LambdaTraceHeaderKey, notSampledTraceHeader);
_recorder.BeginSubsegment("subsegment1");
_recorder.BeginSubsegment("subsegment2");
Subsegment subsegment2 = (Subsegment)AWSXRayRecorder.Instance.TraceContext.GetEntity(); // even if facade segment not sampled, subsegment tree is still available
_recorder.EndSubsegment();
Subsegment subsegment1 = (Subsegment)AWSXRayRecorder.Instance.TraceContext.GetEntity();
Assert.AreEqual("subsegment1", AWSXRayRecorder.Instance.TraceContext.GetEntity().Name);
FacadeSegment facadeSegment = (FacadeSegment)subsegment1.RootSegment;
Assert.IsFalse(facadeSegment.Subsegments.Contains(subsegment2)); // subsegment1 dropped
Assert.IsTrue(facadeSegment.Subsegments.Contains(subsegment1));
_recorder.EndSubsegment();
Assert.IsFalse(AWSXRayRecorder.Instance.TraceContext.IsEntityPresent());
}
[TestMethod]
public void TestBeginSubsegmentWithCustomTime()
{
AWSXRayRecorder recorder = new AWSXRayRecorderBuilder().Build();
var custom_time = new DateTime(2019, 07, 14);
recorder.BeginSubsegment("Subsegment1", custom_time);
Subsegment subsegment = (Subsegment)recorder.TraceContext.GetEntity();
Assert.AreEqual(1563062400, subsegment.StartTime);
recorder.EndSubsegment();
Assert.IsTrue(DateTime.UtcNow.ToUnixTimeSeconds() >= subsegment.EndTime);
}
[TestMethod]
public void TestEndSubsegmentWithCustomTime()
{
AWSXRayRecorder recorder = new AWSXRayRecorderBuilder().Build();
recorder.BeginSubsegment("Subsegment1");
Subsegment subsegment = (Subsegment)recorder.TraceContext.GetEntity();
Assert.IsTrue(DateTime.UtcNow.ToUnixTimeSeconds() >= subsegment.StartTime);
var custom_time = new DateTime(2019, 07, 14);
recorder.EndSubsegment(custom_time);
Assert.AreEqual(1563062400, subsegment.EndTime);
}
}
}