using SQLInsight.Core.Data; using SQLInsight.Core.Findings; using SQLInsight.Rules.Engine; using SQLInsight.Rules.Performance; using static SQLInsight.Rules.Tests.TestInstances; namespace SQLInsight.Rules.Tests; public class AnomalyRuleTests { private static RuleResult Run(RuleContext context) => Assert.Single(new RuleEngine([new AnomalyRule()]).Evaluate(context)); private static BaselineSnapshot Baseline(params (string Metric, double Mean, double Stdev, int Samples)[] metrics) => new() { CapturedAt = Now, HourOfWeek = 37, Metrics = metrics.ToDictionary(m => m.Metric, m => new MetricBaseline(m.Mean, m.Stdev, m.Samples)), }; private static CpuUtilization Cpu(int percent) => new() { CapturedAt = Now, Samples = Enumerable.Range(0, 15).Select(i => new CpuSample(Now.AddMinutes(i - 15), percent, 100 - percent)).ToList(), }; private static CumulativeSample Counters(double batches, long ple) => new( new PerformanceCounterSnapshot { CapturedAt = Now, SqlServerStartTime = Now.UtcDateTime.AddDays(-1), PageLifeExpectancySeconds = ple }, new PerformanceCounterRates { From = Now.AddMinutes(-5), To = Now, BatchRequestsPerSecond = batches }); [Fact] public void Too_little_history_is_not_measurable() { var result = Run(With(Baseline((AnomalyRule.CpuMetric, 20, 5, 3)), Cpu(90))); Assert.Equal(RuleOutcome.NotMeasurable, result.Outcome); Assert.Contains("uger", result.Reason, StringComparison.Ordinal); } [Fact] public void Cpu_far_above_normal_for_this_hour() { var finding = Assert.Single(Run(With(Baseline((AnomalyRule.CpuMetric, 20, 5, 9)), Cpu(75))).Findings); Assert.Equal("anomaly: cpu", finding.Subject); Assert.Contains("tirsdag kl. 13–14 UTC", finding.Evidence[1].Value, StringComparison.Ordinal); } [Fact] public void High_cpu_that_is_normal_for_this_hour_passes() { Assert.Equal(RuleOutcome.Passed, Run(With(Baseline((AnomalyRule.CpuMetric, 80, 8, 9)), Cpu(88))).Outcome); } [Fact] public void Traffic_drop_suggests_an_application_is_down() { var finding = Assert.Single(Run(With(Baseline((AnomalyRule.BatchMetric, 800, 100, 9)), Counters(batches: 40, ple: 5000))).Findings); Assert.Equal(Severity.Medium, finding.Severity); Assert.Contains("færre", finding.Explanation, StringComparison.Ordinal); } [Fact] public void Ple_collapse() { var finding = Assert.Single(Run(With(Baseline((AnomalyRule.PleMetric, 4000, 500, 9)), Counters(batches: 0, ple: 300))).Findings); Assert.Equal("anomaly: ple", finding.Subject); } }