sqlinsight-pro/tests/SQLInsight.IntegrationTests/ReplicationTests.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

38 lines
2.2 KiB
C#

using SQLInsight.Collectors.Instance;
using SQLInsight.Core.Data;
namespace SQLInsight.IntegrationTests;
/// <summary>Replication on a distributor, as the least-privilege agent: not readable without rights, readable with SELECT on the agent tables.</summary>
[Collection(SqlServerTestGroup.Name)]
[Trait("Category", "Integration")]
public class ReplicationTests(SqlServerFixture sql)
{
[Fact]
public async Task A_distributor_is_unreadable_without_rights_and_readable_with_select_on_the_agent_tables()
{
await using var session = await AgentSession.OpenAsync(sql);
var before = await session.CollectAsync<ReplicationSnapshot>(new ReplicationCollector());
Assert.False(before.IsDistributor);
await sql.ExecuteAsSaAsync("DECLARE @s sysname; SET @s = @@SERVERNAME; EXEC sp_adddistributor @distributor = @s, @password = N'Distr-Passw0rd!';");
await sql.ExecuteAsSaAsync("EXEC sp_adddistributiondb @database = N'distribution';");
try
{
var denied = await session.CollectAsync<ReplicationSnapshot>(new ReplicationCollector());
await sql.ExecuteAsSaAsync($"USE distribution; IF USER_ID(N'{SqlServerFixture.AgentLogin}') IS NULL CREATE USER [{SqlServerFixture.AgentLogin}] FOR LOGIN [{SqlServerFixture.AgentLogin}]; GRANT SELECT ON dbo.MSdistribution_agents TO [{SqlServerFixture.AgentLogin}]; GRANT SELECT ON dbo.MSdistribution_history TO [{SqlServerFixture.AgentLogin}]; GRANT SELECT ON dbo.MSrepl_errors TO [{SqlServerFixture.AgentLogin}];");
var readable = await session.CollectAsync<ReplicationSnapshot>(new ReplicationCollector());
Assert.True(denied.IsDistributor);
Assert.Contains("GRANT SELECT ON dbo.MSdistribution_agents", denied.Unavailable, StringComparison.Ordinal);
Assert.True(readable.Unavailable is null, readable.Unavailable);
Assert.Empty(readable.Agents);
Assert.Equal(0, readable.ErrorsLastDay);
}
finally
{
await sql.ExecuteAsSaAsync("EXEC sp_dropdistributiondb @database = N'distribution';");
await sql.ExecuteAsSaAsync("EXEC sp_dropdistributor @no_checks = 1, @ignore_distributor = 1;");
}
}
}