public void ValidateCacheHitOrMiss_SameUserAgent() { string resourceKey = "resource_key"; string userAgent = "iPhone"; _jsonResponse = @"{ ""device"": { ""ismobile"": true } }"; ConfigureMockedClient(r => true); var engine = new CloudRequestEngineBuilder(_loggerFactory, _httpClient) .SetResourceKey(resourceKey) .SetCacheSize(10) .SetCacheHitOrMiss(true) .Build(); using (var pipeline = new PipelineBuilder(_loggerFactory).AddFlowElement(engine).Build()) { var data1 = pipeline.CreateFlowData(); data1.AddEvidence("query.User-Agent", userAgent); data1.Process(); Assert.IsFalse(data1.GetFromElement(engine).CacheHit, "cache miss should occur."); var data2 = pipeline.CreateFlowData(); data2.AddEvidence("query.User-Agent", userAgent); data2.Process(); Assert.IsTrue(data2.GetFromElement(engine).CacheHit, "cache hit should occur."); } }
public void OriginHeader() { string resourceKey = "resource_key"; string origin = "51degrees.com"; string userAgent = "test"; ConfigureMockedClient(r => true); var engine = new CloudRequestEngineBuilder(_loggerFactory, _httpClient) .SetResourceKey(resourceKey) .SetCloudRequestOrigin(origin) .Build(); using (var pipeline = new PipelineBuilder(_loggerFactory).AddFlowElement(engine).Build()) { var data = pipeline.CreateFlowData(); data.AddEvidence("query.User-Agent", userAgent); data.Process(); } _handlerMock.Protected().Verify( "SendAsync", Times.Exactly(1), // we expected a single external request ItExpr.Is <HttpRequestMessage>(req => req.Method == HttpMethod.Post // we expected a POST request // The origin header must contain the expected value && ((req.Content.Headers.Contains(Constants.ORIGIN_HEADER_NAME) && req.Content.Headers.GetValues(Constants.ORIGIN_HEADER_NAME).Contains(origin)) || (req.Headers.Contains(Constants.ORIGIN_HEADER_NAME) && req.Headers.GetValues(Constants.ORIGIN_HEADER_NAME).Contains(origin))) ), ItExpr.IsAny <CancellationToken>() ); }
public void JsonBuilder_LazyLoading() { var engine = new EmptyEngineBuilder(_loggerFactory) .SetLazyLoadingTimeout(1000) .SetProcessCost(TimeSpan.FromMilliseconds(500).Ticks) .Build(); var jsonBuilder = new JsonBuilderElementBuilder(_loggerFactory) .Build(); var sequenceElement = new SequenceElementBuilder(_loggerFactory) .Build(); var pipeline = new PipelineBuilder(_loggerFactory) .AddFlowElement(sequenceElement) .AddFlowElement(engine) .AddFlowElement(jsonBuilder) .Build(); var flowData = pipeline.CreateFlowData(); Trace.WriteLine("Process starting"); flowData.Process(); Trace.WriteLine("Process complete"); var jsonResult = flowData.Get <IJsonBuilderElementData>(); Assert.IsNotNull(jsonResult); Assert.IsNotNull(jsonResult.Json); var jsonData = JsonConvert.DeserializeObject <JsonData>(jsonResult.Json); Assert.AreEqual(1, jsonData.EmptyAspect.Valueone); Assert.AreEqual(2, jsonData.EmptyAspect.Valuetwo); Trace.WriteLine("Data validated"); }
public void RunExample() { //! [usage] // Construct a cloud request engine using the example endpoint for // the example engine to use. var cloudRequestEngine = new CloudRequestEngineBuilder(_loggerFactory, new HttpClient()) .SetEndPoint("http://51degrees.com/starsign/api/") .SetResourceKey("cloudexample") .Build(); // Construct the example engine. var starSignEngine = new SimpleCloudEngineBuilder(_loggerFactory, cloudRequestEngine) .Build(); // Construct the pipeline with the example engine and the cloud // request engine it requires. var pipeline = new PipelineBuilder(_loggerFactory) .AddFlowElement(cloudRequestEngine) .AddFlowElement(starSignEngine) .Build(); var dob = "18/12/1992"; // Create a new flow data. var flowData = pipeline.CreateFlowData(); // Add the evidence and process the data. flowData .AddEvidence("date-of-birth", dob) .Process(); // Now get the result of the processing. Console.WriteLine($"With a date of birth of {dob}, " + $"your star sign is {flowData.Get<IStarSignData>().StarSign}."); //! [usage] }
static void Main(string[] args) { var ct = new CancellationToken(); var cfg = new LazyLoadingConfiguration(10000, ct); var engine = new CloudRequestEngineBuilder(_loggerFactory, _httpClient) .SetEndPoint("https://cloud.51degrees.com/api/v4/json") // Obtain a resource key from https://configure.51degrees.com .SetResourceKey("AQS5HKcyHJbECm6E10g") .SetLazyLoading(cfg) .Build(); using (var pipeline = new PipelineBuilder(_loggerFactory).AddFlowElement(engine).Build()) { var data = pipeline.CreateFlowData(); data.AddEvidence("query.User-Agent", "iPhone"); data.Process(); var result = data.GetFromElement(engine).JsonResponse; Console.WriteLine(result); } Console.ReadKey(); }
public void PipelineIntegration_StopFlag() { // Configure the pipeline var stopElement = new StopElement(); var testElement = new Mock <IFlowElement>(); testElement.SetupGet(e => e.ElementDataKey).Returns("test"); testElement.SetupGet(e => e.Properties).Returns(new List <IElementPropertyMetaData>()); var pipeline = new PipelineBuilder(new LoggerFactory()) .AddFlowElement(stopElement) .AddFlowElement(testElement.Object) .Build(); // Create and process flow data using (var flowData = pipeline.CreateFlowData()) { flowData.Process(); // Check that the stop flag is set #pragma warning disable CS0618 // Type or member is obsolete // This usage will be replaced once the Cancellation Token // mechanism is available. Assert.IsTrue(flowData.Stop); #pragma warning restore CS0618 // Type or member is obsolete } // Check that the second element was never processed testElement.Verify(e => e.Process(It.IsAny <IFlowData>()), Times.Never()); }
public void RunExample() { //! [usage] // Construct the engine. var starSignElement = new SimpleFlowElementBuilder(_loggerFactory) .Build(); // Construct the pipeline with the example engine. var pipeline = new PipelineBuilder(_loggerFactory) .AddFlowElement(starSignElement) .Build(); var dob = new DateTime(1992, 12, 18); // Create a new flow data. var flowData = pipeline.CreateFlowData(); // Add the evidence and process the data. flowData .AddEvidence("date-of-birth", dob) .Process(); // Now get the result of the processing. Console.WriteLine($"With a date of birth of " + $"{dob.ToString("dd/MM/yyy")}" + $", your star sign is " + $"{flowData.Get<IStarSignData>().StarSign}."); //! [usage] }
public void RunExample() { //! [usage] // Construct the engine using the data file. Let's disable auto // updates as we have not implemented it in this example engine. var starSignEngine = new SimpleOnPremiseEngineBuilder(_loggerFactory, null) .SetAutoUpdate(false) .SetDataFileSystemWatcher(false) .Build("starsigns.csv", false); // Construct the pipeline with the example engine. var pipeline = new PipelineBuilder(_loggerFactory) .AddFlowElement(starSignEngine) .Build(); var dob = new DateTime(1992, 12, 18); // Create a new flow data. var flowData = pipeline.CreateFlowData(); // Add the evidence and process the data. flowData .AddEvidence("date-of-birth", dob) .Process(); // Now get the result of the processing. Console.WriteLine($"With a date of birth of {dob}, " + $"your star sign is {flowData.Get<IStarSignData>().StarSign}."); //! [usage] }
public void EvidencePrecedence(bool warn, string evidence1, string evidence2) { var evidence1Parts = evidence1.Split("="); var evidence2Parts = evidence2.Split("="); string resourceKey = "resource_key"; ConfigureMockedClient(r => r.Content.ReadAsStringAsync().Result.Contains(evidence1.Split('.').Last()) ); var loggerFactory = new TestLoggerFactory(); var engine = new CloudRequestEngineBuilder(loggerFactory, _httpClient) .SetResourceKey(resourceKey) .Build(); using (var pipeline = new PipelineBuilder(loggerFactory).AddFlowElement(engine).Build()) { var data = pipeline.CreateFlowData(); data.AddEvidence(evidence1Parts[0], evidence1Parts[1]); data.AddEvidence(evidence2Parts[0], evidence2Parts[1]); data.Process(); } // Get loggers. var loggers = loggerFactory.Loggers .Where(l => l.GetType().IsAssignableFrom(typeof(TestLogger <FlowElements.CloudRequestEngine>))); var logger = loggers.FirstOrDefault(); // If warn is expected then check for warnings from cloud request // engine. if (warn) { logger.AssertMaxWarnings(1); logger.AssertMaxErrors(0); Assert.AreEqual(1, logger.WarningsLogged.Count); var warning = logger.WarningsLogged.Single(); Assert.IsTrue(warning.Contains($"'{evidence1}' evidence conflicts with '{evidence2}'")); } else { logger.AssertMaxWarnings(0); logger.AssertMaxErrors(0); } _handlerMock.Protected().Verify( "SendAsync", Times.Exactly(1), // we expected a single external request ItExpr.Is <HttpRequestMessage>(req => req.Method == HttpMethod.Post && // we expected a POST request req.RequestUri == expectedUri // to this uri ), ItExpr.IsAny <CancellationToken>() ); }
public void Process_NoCloudResponse() { // Setup properties. List <PropertyMetaData> properties = new List <PropertyMetaData>(); properties.Add(new PropertyMetaData() { Name = "ismobile", Type = "Boolean" }); ProductMetaData devicePropertyData = new ProductMetaData(); devicePropertyData.Properties = properties; _propertiesReturnedByRequestEngine.Add("test", devicePropertyData); // Create mock TestInstance so we can see if the ProcessCloudEngine // method is called. var mockTestInstance = new Mock <TestInstance>() { CallBase = true }; mockTestInstance.Protected().Setup( "ProcessCloudEngine", ItExpr.IsAny <IFlowData>(), ItExpr.IsAny <TestData>(), ItExpr.IsAny <string>()).Verifiable(); // Create mock TestRequestEngine var mockRequestEngine = new Mock <TestRequestEngine>() { CallBase = true }; mockRequestEngine.Object.PublicProperties = _propertiesReturnedByRequestEngine; // Construct the pipeline. var pipeline = new PipelineBuilder(new LoggerFactory()) .AddFlowElement(mockRequestEngine.Object) .AddFlowElement(mockTestInstance.Object) .Build(); // Process the pipeline. using (var flowData = pipeline.CreateFlowData()) { flowData .AddEvidence("query.user-agent", "iPhone") .Process(); } // Verify the ProcessCloudEngine method was called mockTestInstance.Protected().Verify( "ProcessCloudEngine", Times.Never(), ItExpr.IsAny <IFlowData>(), ItExpr.IsAny <TestData>(), ItExpr.IsAny <string>()); }
public void ValidateCacheHitOrMiss_DifferentUserAgent_SameLocation(bool hit, params string[] evidenceKeys) { string resourceKey = "resource_key"; string userAgent1 = "iPhone"; string userAgent2 = "Samsung"; string latlon = "51"; _evidenceKeysResponse = $"[ '{string.Join("', '", evidenceKeys)}' ]"; _jsonResponse = @"{ ""device"": { ""ismobile"": true } }"; ConfigureMockedClient(r => true); var engine = new CloudRequestEngineBuilder(_loggerFactory, _httpClient) .SetResourceKey(resourceKey) .SetCacheSize(10) .SetCacheHitOrMiss(true) .Build(); using (var pipeline = new PipelineBuilder(_loggerFactory).AddFlowElement(engine).Build()) { var data1 = pipeline.CreateFlowData(); data1.AddEvidence("query.User-Agent", userAgent1); data1.AddEvidence("query.51d_pos_latitude", latlon); data1.AddEvidence("query.51d_pos_longitude", latlon); data1.Process(); Assert.IsFalse(data1.GetFromElement(engine).CacheHit, "cache miss should occur."); var data2 = pipeline.CreateFlowData(); data2.AddEvidence("query.User-Agent", userAgent2); data2.AddEvidence("query.51d_pos_latitude", latlon); data2.AddEvidence("query.51d_pos_longitude", latlon); data2.Process(); Assert.AreEqual(hit, data2.GetFromElement(engine).CacheHit, $"cache hit {(hit ? "should" : "shouldn't")} occur."); } }
public void AspectEngineLazyLoad_Itegrated() { // Arrange // Set the process time to half of the configured timeout var processCostMs = _timeoutMS / 2; _engine.SetProcessCost(TimeSpan.TicksPerMillisecond * processCostMs); var pipeline = new PipelineBuilder(_loggerFactory) .AddFlowElement(_engine) .Build(); // Act var stopwatch = new Stopwatch(); var flowData = pipeline.CreateFlowData(); Trace.WriteLine("Process starting"); stopwatch.Start(); flowData.Process(); long processTimeMs = stopwatch.ElapsedMilliseconds; Trace.WriteLine($"Process complete in {processTimeMs} ms"); Assert.IsTrue(processTimeMs < processCostMs, $"Process time should have been less than " + $"{processCostMs} ms but it took {processTimeMs} ms."); // Assert var data = flowData.Get <EmptyEngineData>(); Assert.IsNotNull(data); Assert.AreEqual(1, data.ValueOne); long valueOneTimeMs = stopwatch.ElapsedMilliseconds; Trace.WriteLine($"Value one accessed after {valueOneTimeMs} ms"); Assert.AreEqual(2, data.ValueTwo); long valueTwoTimeMs = stopwatch.ElapsedMilliseconds; Trace.WriteLine($"Value two accessed after {valueTwoTimeMs} ms"); Assert.IsTrue(valueOneTimeMs < processCostMs, $"Accessing value one should have taken less than " + $"{processCostMs} ms from the time the Process method" + $"was called but it took {valueOneTimeMs} ms."); // Note - this should really take at least 'processCostMs' // but the accuracy of the timer seems to cause issues // if we are being that exact. Assert.IsTrue(valueTwoTimeMs >= processCostMs / 2, $"Accessing value two should have taken at least " + $"{processCostMs / 2} ms from the time the Process method" + $"was called but it only took {valueTwoTimeMs} ms."); }
public void ValidateCacheHitOrMiss_SameUserAgent_AdditionalHeaders() { string resourceKey = "resource_key"; string userAgent = "iPhone"; string xOperaMiniUA1 = "SonyEricsson/W810i"; string xOperaMiniUA2 = "Nokia/3310"; _evidenceKeysResponse = "[ 'query.User-Agent', 'header.X-OperaMini-Phone-UA' ]"; _jsonResponse = @"{ ""device"": { ""ismobile"": true } }"; ConfigureMockedClient(r => true); var engine = new CloudRequestEngineBuilder(_loggerFactory, _httpClient) .SetResourceKey(resourceKey) .SetCacheSize(10) .SetCacheHitOrMiss(true) .Build(); using (var pipeline = new PipelineBuilder(_loggerFactory).AddFlowElement(engine).Build()) { var data1 = pipeline.CreateFlowData(); data1.AddEvidence("query.User-Agent", userAgent); data1.AddEvidence("header.X-OperaMini-Phone-UA", xOperaMiniUA1); data1.Process(); Assert.IsFalse(data1.GetFromElement(engine).CacheHit, "cache miss should occur."); var data2 = pipeline.CreateFlowData(); data2.AddEvidence("query.User-Agent", userAgent); data2.AddEvidence("header.X-OperaMini-Phone-UA", xOperaMiniUA2); data2.Process(); Assert.IsFalse(data2.GetFromElement(engine).CacheHit, "cache miss should occur."); } }
public void PipelineIntegration_SingleElement() { var fiveElement = new MultiplyByFiveElement(); var pipeline = new PipelineBuilder(new LoggerFactory()) .AddFlowElement(fiveElement) .Build(); var flowData = pipeline.CreateFlowData(); flowData.AddEvidence(fiveElement.EvidenceKeys[0], 2); flowData.Process(); Assert.AreEqual(10, flowData.GetFromElement(fiveElement).Result); }
public void ValidateErrorHandling_MultipleErrors() { string resourceKey = "resource_key"; string userAgent = "iPhone"; _jsonResponse = @"{ ""errors"": [""This resource key is not authorized for use with this domain: . Please visit https://configure.51degrees.com to update your resource key."",""Some other error""] }"; ConfigureMockedClient(r => r.Content.ReadAsStringAsync().Result.Contains($"resource={resourceKey}") && // content contains resource key r.Content.ReadAsStringAsync().Result.Contains($"User-Agent={userAgent}") // content contains licenseKey ); var engine = new CloudRequestEngineBuilder(_loggerFactory, _httpClient) .SetResourceKey(resourceKey) .Build(); Exception exception = null; try { using (var pipeline = new PipelineBuilder(_loggerFactory).AddFlowElement(engine).Build()) { var data = pipeline.CreateFlowData(); data.AddEvidence("query.User-Agent", userAgent); data.Process(); } } catch (Exception ex) { exception = ex; } Assert.IsNotNull(exception, "Expected exception to occur"); Assert.IsInstanceOfType(exception, typeof(AggregateException)); var aggEx = (exception as AggregateException).Flatten(); Assert.AreEqual(aggEx.InnerExceptions.Count, 2); Assert.IsInstanceOfType(aggEx.InnerExceptions[0], typeof(PipelineException)); Assert.IsInstanceOfType(aggEx.InnerExceptions[1], typeof(PipelineException)); Assert.IsTrue(aggEx.InnerExceptions.Any(e => e.Message.Contains( "This resource key is not authorized for use with this domain")), "Exception message did not contain the expected text."); Assert.IsTrue(aggEx.InnerExceptions.Any(e => e.Message.Contains( "Some other error")), "Exception message did not contain the expected text."); }
public void Process_LicenseKey() { string resourceKey = "resource_key"; string userAgent = "iPhone"; string licenseKey = "ABCDEFG"; ConfigureMockedClient(r => r.Content.ReadAsStringAsync().Result.Contains($"resource={resourceKey}") && // content contains resource key r.Content.ReadAsStringAsync().Result.Contains($"license={licenseKey}") && // content contains licenseKey r.Content.ReadAsStringAsync().Result.Contains($"User-Agent={userAgent}") // content contains user agent ); #pragma warning disable CS0618 // Type or member is obsolete // SetLicensekey is obsolete but we still want to test that // it works as intended. var engine = new CloudRequestEngineBuilder(_loggerFactory, _httpClient) .SetResourceKey(resourceKey) .SetLicenseKey(licenseKey) #pragma warning restore CS0618 // Type or member is obsolete .Build(); using (var pipeline = new PipelineBuilder(_loggerFactory).AddFlowElement(engine).Build()) { var data = pipeline.CreateFlowData(); data.AddEvidence("query.User-Agent", userAgent); data.Process(); var result = data.GetFromElement(engine).JsonResponse; Assert.AreEqual("{'device':{'value':'1'}}", result); dynamic obj = JValue.Parse(result); Assert.AreEqual(1, (int)obj.device.value); } _handlerMock.Protected().Verify( "SendAsync", Times.Exactly(1), // we expected a single external request ItExpr.Is <HttpRequestMessage>(req => req.Method == HttpMethod.Post && // we expected a POST request req.RequestUri == expectedUri // to this uri ), ItExpr.IsAny <CancellationToken>() ); }
public void PipelineBuilder_BuildFromConfiguration_ServiceCollection() { var element = new ElementOptions() { BuilderName = "MultiplyByElementBuilder" }; element.BuildParameters.Add("multiple", "3"); // Create the configuration object. PipelineOptions opts = new PipelineOptions(); opts.Elements = new List <ElementOptions> { element }; Mock <IServiceProvider> services = new Mock <IServiceProvider>(); services.Setup(s => s.GetService(typeof(MultiplyByElementBuilder))) .Returns(new MultiplyByElementBuilder()); // Pass the configuration to the builder to create the pipeline. var pipeline = new PipelineBuilder(_loggerFactory, services.Object) .BuildFromConfiguration(opts); // Get the element var multiplyByElement = pipeline.GetElement <MultiplyByElement>(); // Create, populate and process flow data. using (var flowData = pipeline.CreateFlowData()) { flowData .AddEvidence(multiplyByElement.EvidenceKeys[0], 25) .Process(); // Get the results and verify them. var multiplyByData = flowData.GetFromElement(multiplyByElement); Assert.AreEqual(75, multiplyByData.Result); } }
public void AspectEngineLazyLoad_Itegrated_AsDictionary() { // Arrange var processCostMs = _timeoutMS / 2; _engine.SetProcessCost(TimeSpan.TicksPerMillisecond * processCostMs); var pipeline = new PipelineBuilder(_loggerFactory) .AddFlowElement(_engine) .Build(); // Act var stopwatch = new Stopwatch(); var flowData = pipeline.CreateFlowData(); Trace.WriteLine("Process starting"); stopwatch.Start(); flowData.Process(); long processTimeMs = stopwatch.ElapsedMilliseconds; Trace.WriteLine($"Process complete in {processTimeMs} ms"); // Assert var data = flowData.Get <EmptyEngineData>(); Assert.IsNotNull(data); var dictionary = data.AsDictionary(); long dictTimeMs = stopwatch.ElapsedMilliseconds; Trace.WriteLine($"Dictionary retrieved after {dictTimeMs} ms"); Assert.AreEqual(1, dictionary[EmptyEngineData.VALUE_ONE_KEY]); Assert.AreEqual(2, dictionary[EmptyEngineData.VALUE_TWO_KEY]); // Note - this should really take at least 'processCostMs' // but the accuracy of the timer seems to cause issues // if we are being that exact. Assert.IsTrue(dictTimeMs > processCostMs / 2, $"Accessing the dictionary should have taken at least " + $"{processCostMs / 2} ms from the time the Process method" + $"was called but it only took {dictTimeMs} ms."); }
public void ValidateErrorHandling_NoData() { string resourceKey = "resource_key"; string userAgent = "iPhone"; _jsonResponse = @"{ }"; ConfigureMockedClient(r => r.Content.ReadAsStringAsync().Result.Contains($"resource={resourceKey}") && // content contains resource key r.Content.ReadAsStringAsync().Result.Contains($"User-Agent={userAgent}") // content contains licenseKey ); var engine = new CloudRequestEngineBuilder(_loggerFactory, _httpClient) .SetResourceKey(resourceKey) .Build(); using (var pipeline = new PipelineBuilder(_loggerFactory).AddFlowElement(engine).Build()) { var data = pipeline.CreateFlowData(); data.AddEvidence("query.User-Agent", userAgent); data.Process(); } }
public void EvidencePrecedenceMultipleConflicts(params string[] evidence) { string resourceKey = "resource_key"; // Get a list of evidence that should not be in the result. var excludedEvidence = evidence .Select(e => e.Split('.').Last()) .Distinct() .Where(e => e != evidence[0].Split('.').Last()); ConfigureMockedClient(r => { var valid = true || excludedEvidence.Count() > 0; // Check that excluded evidence is not in the result. foreach (var item in excludedEvidence) { valid = r.Content.ReadAsStringAsync().Result.Contains(item) == false; if (valid == false) { break; } } // Check that the expected evidence is in the result. return(r.Content.ReadAsStringAsync().Result.Contains(evidence[0].Split('.').Last()) && valid); }); var loggerFactory = new TestLoggerFactory(); var engine = new CloudRequestEngineBuilder(loggerFactory, _httpClient) .SetResourceKey(resourceKey) .Build(); using (var pipeline = new PipelineBuilder(loggerFactory).AddFlowElement(engine).Build()) { var data = pipeline.CreateFlowData(); foreach (var item in evidence) { var evidenceParts = item.Split("="); data.AddEvidence(evidenceParts[0], evidenceParts[1]); } data.Process(); } // Get loggers. var loggers = loggerFactory.Loggers .Where(l => l.GetType().IsAssignableFrom(typeof(TestLogger <FlowElements.CloudRequestEngine>))); var logger = loggers.FirstOrDefault(); // Check that the expected number of warnings has been logged. logger.AssertMaxWarnings(evidence.Length - 1); logger.AssertMaxErrors(0); Assert.AreEqual(evidence.Length - 1, logger.WarningsLogged.Count); // Check that only conflict warnings have been logged. foreach (var warning in logger.WarningsLogged) { Assert.IsTrue(warning.Contains("evidence conflicts")); } _handlerMock.Protected().Verify( "SendAsync", Times.Exactly(1), // we expected a single external request ItExpr.Is <HttpRequestMessage>(req => req.Method == HttpMethod.Post && // we expected a POST request req.RequestUri == expectedUri // to this uri ), ItExpr.IsAny <CancellationToken>() ); }
public void CloudEngines_WrongOrder() { // Setup properties. List <PropertyMetaData> properties = new List <PropertyMetaData>(); properties.Add(new PropertyMetaData() { Name = "ismobile", Type = "Boolean" }); ProductMetaData devicePropertyData = new ProductMetaData(); devicePropertyData.Properties = properties; _propertiesReturnedByRequestEngine.Add("test", devicePropertyData); // Create mock TestInstance so we can see if the ProcessCloudEngine // method is called. var mockTestInstance = new Mock <TestInstance>() { CallBase = true }; mockTestInstance.Protected().Setup( "ProcessCloudEngine", ItExpr.IsAny <IFlowData>(), ItExpr.IsAny <TestData>(), ItExpr.IsAny <string>()).Verifiable(); // Create mock TestRequestEngine so we can mock the ProcessEngine // method and get it to set it's aspect data. var mockRequestEngine = new Mock <TestRequestEngine>() { CallBase = true }; mockRequestEngine.Object.PublicProperties = _propertiesReturnedByRequestEngine; mockRequestEngine.Protected() .Setup( "ProcessEngine", ItExpr.IsAny <IFlowData>(), ItExpr.IsAny <CloudRequestData>()) .Callback((IFlowData data, CloudRequestData aspectData) => { aspectData.JsonResponse = "{ \"response\": true }"; aspectData.ProcessStarted = true; }); // Construct the pipeline. var pipeline = new PipelineBuilder(new LoggerFactory()) .AddFlowElement(mockTestInstance.Object) .AddFlowElement(mockRequestEngine.Object) .Build(); // Process the pipeline. try { using (var flowData = pipeline.CreateFlowData()) { flowData .AddEvidence("query.user-agent", "iPhone") .Process(); } } catch (AggregateException ex) { var expectedExceptions = ex.InnerExceptions .Where(e => e.GetType() == typeof(PipelineConfigurationException)); Assert.IsTrue( expectedExceptions.Count() > 0, "Aggregate exception did not contain expected PipelineConfigurationException."); Assert.AreEqual( "The 'TestInstanceProxy' requires a 'CloudRequestEngine' " + "before it in the Pipeline. This engine will be unable to " + "produce results until this is corrected.", expectedExceptions.FirstOrDefault()?.Message, "PipelineConfigurationException message was incorrect."); } }