sqlinsight-pro/tests/SQLInsight.Rules.Tests/ReplicationRuleTests.cs
Søren Eggert Lundsteen Olsen 473a7d0be3 Replication from the distributor (8f)
On a distributor, a 15-minute collector reads each distribution agent's
latest run (status, latency, message) and replication errors the last
day from the distribution database. That needs SELECT on
MSdistribution_agents, MSdistribution_history and MSrepl_errors, which
the agent does not have by default (never the replicated commands);
without it SQLI-REPL-001 (agent failed/retrying, High) and SQLI-REPL-002
(latency > 5 min, Medium) are "not measurable" with the exact grant, and
"not relevant" elsewhere. Shown on the instance page and in
GET /api/v1/instances/{name}/replication. Tested against a real
distributor set up in the SQL 2022 container.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:48:46 +02:00

40 lines
2 KiB
C#

using SQLInsight.Core.Data;
using SQLInsight.Core.Findings;
using SQLInsight.Rules.Availability;
using SQLInsight.Rules.Engine;
using static SQLInsight.Rules.Tests.TestInstances;
namespace SQLInsight.Rules.Tests;
public class ReplicationRuleTests
{
private static RuleResult Run(IRule rule, RuleContext context) => Assert.Single(new RuleEngine([rule]).Evaluate(context));
private static ReplicationSnapshot Distributor(params DistributionAgent[] agents) =>
new() { CapturedAt = Now, IsDistributor = true, DistributionDatabase = "distribution", Agents = agents };
[Fact]
public void Not_a_distributor_is_not_relevant_and_without_rights_not_measurable()
{
Assert.Equal(RuleOutcome.NotApplicable, Run(new ReplicationAgentFailedRule(), With(new ReplicationSnapshot { CapturedAt = Now })).Outcome);
var denied = Run(new ReplicationLatencyRule(), With(Distributor() with { Unavailable = "Giv agenten SELECT på MSdistribution_agents" }));
Assert.Equal(RuleOutcome.NotMeasurable, denied.Outcome);
Assert.Contains("MSdistribution_agents", denied.Reason, StringComparison.Ordinal);
}
[Fact]
public void Failed_agents_are_high_and_slow_ones_medium()
{
var failed = new DistributionAgent("SQL01-AX-Pub-SQL02-1", "AX", 6, Now, null, "The process could not connect to Subscriber 'SQL02'.");
var slow = new DistributionAgent("SQL01-AX-Pub-SQL03-2", "AX", 3, Now, 600_000, null);
var fine = new DistributionAgent("SQL01-AX-Pub-SQL04-3", "AX", 4, Now, 2_000, null);
var errors = Run(new ReplicationAgentFailedRule(), With(Distributor(failed, slow, fine)));
var latency = Run(new ReplicationLatencyRule(), With(Distributor(failed, slow, fine)));
var finding = Assert.Single(errors.Findings);
Assert.Equal(Severity.High, finding.Severity);
Assert.True(finding.Evidence.Single(e => e.Name == "Meddelelse").Sensitive);
Assert.Contains("SQL03", Assert.Single(latency.Findings).Evidence.Single(e => e.Name == "Agent").Value, StringComparison.Ordinal);
}
}