/// <summary>
        /// Creating a new instance of <see cref="EmulationAlert"/> for unit tests.
        /// </summary>
        /// <param name="alert">The alert to wrap</param>
        /// <returns>An emulation alert</returns>
        public static EmulationAlert CreateEmulationAlert(Alert alert)
        {
            string resourceId = alert.ResourceIdentifier.ResourceType == ResourceType.ApplicationInsights ?
                                appInsightsResourceId :
                                virtualMachineResourceId;

            QueryRunInfo queryRunInfo = new QueryRunInfo
            {
                Type        = alert.ResourceIdentifier.ResourceType == ResourceType.ApplicationInsights ? TelemetryDbType.ApplicationInsights : TelemetryDbType.LogAnalytics,
                ResourceIds = new List <string>()
                {
                    resourceId
                }
            };

            var request = new SmartDetectorExecutionRequest
            {
                ResourceIds = new List <string>()
                {
                    resourceId
                },
                SmartDetectorId = "smartDetectorId",
                Cadence         = TimeSpan.FromDays(1),
            };

            ContractsAlert contractsAlert = alert.CreateContractsAlert(request, "smartDetectorName", queryRunInfo, usedLogAnalysisClient: false, usedMetricClient: false);

            return(new EmulationAlert(contractsAlert, ExtendedDateTime.UtcNow));
        }
        private void AssertAlertProperties(ContractsAlert contractsAlert)
        {
            Assert.AreEqual(4, contractsAlert.AlertProperties.Count);

            int propertyIndex = 0;
            Assert.AreEqual("Predicate", contractsAlert.AlertProperties[propertyIndex].PropertyName);
            Assert.AreEqual(AlertPropertyType.Raw, contractsAlert.AlertProperties[propertyIndex].Type);
            Assert.AreEqual("AlertTitle", ((RawAlertProperty)contractsAlert.AlertProperties[propertyIndex]).Value);

            propertyIndex++;
            Assert.AreEqual("RawProperty", contractsAlert.AlertProperties[propertyIndex].PropertyName);
            Assert.AreEqual(AlertPropertyType.Raw, contractsAlert.AlertProperties[propertyIndex].Type);
            Assert.AreEqual(1, ((RawAlertProperty)contractsAlert.AlertProperties[propertyIndex]).Value);

            propertyIndex++;
            Assert.AreEqual("LongTextPropertyName", contractsAlert.AlertProperties[propertyIndex].PropertyName);
            Assert.AreEqual(AlertPropertyType.LongText, contractsAlert.AlertProperties[propertyIndex].Type);
            Assert.AreEqual("LongTextDisplayName", ((LongTextAlertProperty)contractsAlert.AlertProperties[propertyIndex]).DisplayName);
            Assert.AreEqual(0, ((LongTextAlertProperty)contractsAlert.AlertProperties[propertyIndex]).Order);
            Assert.AreEqual("LongTextValue", ((LongTextAlertProperty)contractsAlert.AlertProperties[propertyIndex]).Value);

            propertyIndex++;
            Assert.AreEqual("TextValue", contractsAlert.AlertProperties[propertyIndex].PropertyName);
            Assert.AreEqual(AlertPropertyType.Text, contractsAlert.AlertProperties[propertyIndex].Type);
            Assert.AreEqual("TextDisplayName", ((TextAlertProperty)contractsAlert.AlertProperties[propertyIndex]).DisplayName);
            Assert.AreEqual(1, ((TextAlertProperty)contractsAlert.AlertProperties[propertyIndex]).Order);
            Assert.AreEqual("TextValue", ((TextAlertProperty)contractsAlert.AlertProperties[propertyIndex]).Value);
        }
        public void WhenProcessingAlertWithV2PresentationThenTheContractsAlertIsCreatedCorrectly()
        {
            ContractsAlert contractsAlert = CreateContractsV2Alert(new PresentationTestAlert());

            Assert.AreEqual(ContractsAlertState.Active, contractsAlert.State);
            Assert.IsTrue(contractsAlert.AnalysisTimestamp <= DateTime.UtcNow, "Unexpected analysis timestamp in the future");
            Assert.IsTrue(contractsAlert.AnalysisTimestamp >= DateTime.UtcNow.AddMinutes(-1), "Unexpected analysis timestamp - too back in the past");
            Assert.AreEqual(24 * 60, contractsAlert.AnalysisWindowSizeInMinutes, "Unexpected analysis window size");
            Assert.AreEqual(SmartDetectorName, contractsAlert.SmartDetectorName, "Unexpected Smart Detector name");
            Assert.AreEqual("AlertTitle", contractsAlert.Title, "Unexpected title");
            Assert.AreEqual(default(ResourceIdentifier).ToResourceId(), contractsAlert.ResourceId, "Unexpected ResourceId");
            Assert.AreEqual(SignalType.Log, contractsAlert.SignalType, "Unexpected signal type");
            Assert.AreEqual(10, contractsAlert.AlertProperties.Count, "Unexpected number of properties");

            // Verify raw alert properties
            VerifyPresentationTestAlertRawProperty(contractsAlert.AlertProperties, "Predicate");
            VerifyPresentationTestAlertRawProperty(contractsAlert.AlertProperties, "RawProperty");

            // Verify displayed alert properties
            VerifyPresentationTestAlertDisplayedProperty(contractsAlert.AlertProperties, "LongTextPropertyName", "LongTextDisplayName", 0);
            VerifyPresentationTestAlertDisplayedProperty(contractsAlert.AlertProperties, "UrlValue", "UrlDisplayName", 1);
            VerifyPresentationTestAlertDisplayedProperty(contractsAlert.AlertProperties, "TextValue", "TextDisplayName", 2);
            VerifyPresentationTestAlertDisplayedProperty(contractsAlert.AlertProperties, "KeyValue", "KeyValueDisplayName", 3);
            VerifyPresentationTestAlertDisplayedProperty(contractsAlert.AlertProperties, "KeyValueWithHeaders", "KeyValueWithHeadersDisplayName", 4);
            VerifyPresentationTestAlertDisplayedProperty(contractsAlert.AlertProperties, "Table", "TableDisplayName", 5);
            VerifyPresentationTestAlertDisplayedProperty(contractsAlert.AlertProperties, "SingleColumnTable", "SingleColumnTableDisplayName", 6);
            VerifyPresentationTestAlertDisplayedProperty(contractsAlert.AlertProperties, "DataPoints", "ChartDisplayName", byte.MaxValue);
        }
        public void WhenCreatingContractsAlertThenAlertDataIsCorrect()
        {
            var resourceId = new ResourceIdentifier(ResourceType.ApplicationInsights, "subscription", "resourceGroup", "myApp");
            var alert = new TestAlert(resourceIdentifier: resourceId);

            ContractsAlert contractsAlert = alert.CreateContractsAlert(this.analysisRequest, SmartDetectorName, false, false);

            Assert.AreEqual("AlertTitle", contractsAlert.Title);
            Assert.AreEqual("/subscriptions/subscription/resourceGroups/resourceGroup/providers/Microsoft.Insights/components/myApp", contractsAlert.ResourceId);
            Assert.AreEqual(alert.OccurenceTime, contractsAlert.OccurenceTime);
            Assert.AreEqual("smartDetectorId", contractsAlert.SmartDetectorId);
            Assert.AreEqual(SmartDetectorName, contractsAlert.SmartDetectorName);
            Assert.AreEqual((int)this.analysisRequest.Cadence.TotalMinutes, contractsAlert.AnalysisWindowSizeInMinutes);
            Assert.AreEqual(SignalType.Log, contractsAlert.SignalType);
            Assert.IsNull(contractsAlert.ResolutionParameters);
            this.AssertAlertProperties(contractsAlert);
        }
        public void WhenProcessingAlertThenTheContractsAlertIsCreatedCorrectly()
        {
            ContractsAlert contractsAlert = CreateContractsAlert(new TestAlert());

            Assert.AreEqual(ContractsAlertState.Active, contractsAlert.State);
            Assert.IsTrue(contractsAlert.AnalysisTimestamp <= DateTime.UtcNow, "Unexpected analysis timestamp in the future");
            Assert.IsTrue(contractsAlert.AnalysisTimestamp >= DateTime.UtcNow.AddMinutes(-1), "Unexpected analysis timestamp - too back in the past");
            Assert.AreEqual(24 * 60, contractsAlert.AnalysisWindowSizeInMinutes, "Unexpected analysis window size");
            Assert.AreEqual(SmartDetectorName, contractsAlert.SmartDetectorName, "Unexpected Smart Detector name");
            Assert.AreEqual("Test title", contractsAlert.Title, "Unexpected title");
            Assert.AreEqual(SignalType.Log, contractsAlert.SignalType, "Unexpected signal type");
            Assert.AreEqual(8, contractsAlert.Properties.Count, "Unexpected number of properties");
            VerifyProperty(contractsAlert.Properties, "Machine name", AlertPresentationSection.Property, "strongOne", "The machine on which the CPU had increased", 1);
            VerifyProperty(contractsAlert.Properties, "CPU over the last 7 days", AlertPresentationSection.Chart, "<the query>", "CPU chart for machine strongOne, showing increase of 22.4");
            VerifyProperty(contractsAlert.Properties, "CPU increased", AlertPresentationSection.Property, "22.4", "CPU increase on machine strongOne");
            VerifyProperty(contractsAlert.Properties, "Another query 1", AlertPresentationSection.AdditionalQuery, "<query1>", "Info balloon for another query 1");
            VerifyProperty(contractsAlert.Properties, "Another query 2", AlertPresentationSection.AdditionalQuery, "<query2>", "Info balloon for another query 2");
            VerifyProperty(contractsAlert.Properties, "Analysis 1", AlertPresentationSection.Analysis, "analysis1", "Info balloon for analysis 1");
            VerifyProperty(contractsAlert.Properties, "Analysis 2", AlertPresentationSection.Analysis, "analysis2", "Info balloon for analysis 2");
            VerifyProperty(contractsAlert.Properties, "Analysis 3", AlertPresentationSection.Analysis, new DateTime(2012, 11, 12, 17, 22, 37).ToString("u", CultureInfo.InvariantCulture), "Info balloon for analysis 3");
            Assert.AreEqual("no show", contractsAlert.RawProperties["NoPresentation"]);
            Assert.AreEqual(TelemetryDbType.LogAnalytics, contractsAlert.QueryRunInfo.Type, "Unexpected telemetry DB type");
            CollectionAssert.AreEqual(new[] { "resourceId1", "resourceId2" }, contractsAlert.QueryRunInfo.ResourceIds.ToArray(), "Unexpected resource IDs");
        }
        /// <summary>
        /// Runs the Smart Detector's analysis flow.
        /// </summary>
        /// <param name="request">The Smart Detector analysis request</param>
        /// <param name="smartDetector">The Smart Detector to run</param>
        /// <param name="smartDetectorManifest">The Smart Detector's manifest</param>
        /// <param name="detectorTracer">The tracer to provider for the Smart Detector</param>
        /// <param name="cancellationToken">The cancellation token</param>
        /// <returns>A <see cref="Task{TResult}"/>, returning the list of Alerts generated by the Smart Detector.</returns>
        private async Task <List <ContractsAlert> > AnalyzeAsync(
            SmartDetectorAnalysisRequest request,
            ISmartDetector smartDetector,
            SmartDetectorManifest smartDetectorManifest,
            ITracer detectorTracer,
            CancellationToken cancellationToken)
        {
            // Create state repository
            IStateRepository stateRepository = this.stateRepositoryFactory.Create(request.SmartDetectorId, request.AlertRuleResourceId);

            // Create the input for the Smart Detector
            AnalysisRequestParameters analysisRequestParameters = await this.CreateAnalysisRequestParametersAsync(DateTime.UtcNow, request, smartDetectorManifest, true, cancellationToken);

            var analysisRequest = new AnalysisRequest(analysisRequestParameters, this.analysisServicesFactory, stateRepository);

            // Run the Smart Detector
            this.tracer.TraceInformation($"Started running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name}");
            List <Alert> alerts;

            try
            {
                alerts = await smartDetector.AnalyzeResourcesAsync(analysisRequest, detectorTracer, cancellationToken);

                this.tracer.TraceInformation(
                    $"Completed running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name}, returning {alerts.Count} alerts");
            }
            catch (DetectorNotReadyException dnre)
            {
                this.tracer.TraceWarning($"Smart Detector is not ready to run analysis yet, aborting analysis: {dnre.Message}");
                return(new List <ContractsAlert>());
            }
            catch (Exception e)
            {
                this.tracer.TraceError($"Failed running Smart Detector ID {smartDetectorManifest.Id}, Name {smartDetectorManifest.Name}: {e}");
                throw new FailedToRunSmartDetectorException($"Calling Smart Detector '{smartDetectorManifest.Name}' failed with exception of type {e.GetType()} and message: {e.Message}", e);
            }

            // Verify that each alert belongs to one of the types declared in the Smart Detector manifest
            foreach (Alert alert in alerts)
            {
                if (!smartDetectorManifest.SupportedResourceTypes.Contains(alert.ResourceIdentifier.ResourceType))
                {
                    throw new UnidentifiedAlertResourceTypeException(alert.ResourceIdentifier);
                }
            }

            // Trace the number of alerts of each type
            foreach (var alertType in alerts.GroupBy(x => x.GetType().Name))
            {
                this.tracer.TraceInformation($"Got {alertType.Count()} Alerts of type '{alertType.Key}'");
                this.tracer.ReportMetric("AlertType", alertType.Count(), new Dictionary <string, string>()
                {
                    { "AlertType", alertType.Key }
                });
            }

            // Create results
            bool detectorSupportsAlertResolution = smartDetector is IResolvableAlertSmartDetector;
            List <ContractsAlert> results        = new List <ContractsAlert>();

            foreach (var alert in alerts)
            {
                ContractsAlert contractsAlert = alert.CreateContractsAlert(
                    request,
                    smartDetectorManifest.Name,
                    this.analysisServicesFactory.UsedLogAnalysisClient,
                    this.analysisServicesFactory.UsedMetricClient);

                // Handle resolution parameters in the alerts:
                // If the detector supports resolution - save the predicates for the resolution checks
                // If the detector doesn't support resolution - drop the resolution parameters (since they are useless) and error trace
                if (contractsAlert.ResolutionParameters != null)
                {
                    if (detectorSupportsAlertResolution)
                    {
                        this.tracer.TraceInformation($"Alert {contractsAlert.CorrelationHash} has resolution parameters, so saving alert details for later use");
                        await stateRepository.StoreStateAsync(
                            GetResolutionStateKey(contractsAlert.CorrelationHash),
                            new ResolutionState
                        {
                            AnalysisRequestTime = analysisRequestParameters.RequestTime,
                            AlertPredicates     = alert.ExtractPredicates()
                        },
                            cancellationToken);
                    }
                    else
                    {
                        this.tracer.TraceError($"Dropping resolution parameters from alert {contractsAlert.CorrelationHash}");
                        contractsAlert.ResolutionParameters = null;
                    }
                }

                // And add the alert to the results
                results.Add(contractsAlert);
            }

            this.tracer.TraceInformation($"Returning {results.Count} results");
            return(results);
        }
        public void WhenProcessingAlertWithStateResolvedThenTheContractsAlertIsCreatedWithCorrectState()
        {
            ContractsAlert contractsAlert = CreateContractsAlert(new TestAlert(AlertState.Resolved));

            Assert.AreEqual(ContractsAlertState.Resolved, contractsAlert.State);
        }
        public void WhenProcessingAlertAndLogAndMetricClientsWereUsedThenTheSignalTypeIsCorrent()
        {
            ContractsAlert contractsAlert = CreateContractsAlert(new TestAlert(), usedLogAnalysisClient: true, usedMetricClient: true);

            Assert.AreEqual(SignalType.Multiple, contractsAlert.SignalType, "Unexpected signal type");
        }
        public void WhenProcessingAlertAndMetricClientWasUsedThenTheSignalTypeIsCorrect()
        {
            ContractsAlert contractsAlert = CreateContractsV2Alert(new PresentationTestAlert(), usedMetricClient: true);

            Assert.AreEqual(SignalType.Metric, contractsAlert.SignalType, "Unexpected signal type");
        }
        public void WhenProcessingAlertWithStateResolvedThenTheContractsAlertIsCreatedWithCorrectState()
        {
            ContractsAlert contractsAlert = CreateContractsV2Alert(new PresentationTestAlert("AlertTitle", default(ResourceIdentifier), AlertState.Resolved));

            Assert.AreEqual(ContractsAlertState.Resolved, contractsAlert.State);
        }