Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
- Availability group collector (replica and database replica states, queues, commit times) and rules: SQLI-AG-001 unhealthy/disconnected replica or suspended data movement (scripts HADR RESUME), SQLI-AG-002 secondary behind (estimated data loss and redo time) - SQL Agent collector using only granted msdb tables (no EXECUTE on msdb functions): SQLI-JOB-001 failed job with the failing step's message (backup/CHECKDB jobs are High), SQLI-JOB-002 SQL Agent not running - Integration tests create a real availability group (CLUSTER_TYPE NONE) and a failing job in the test container (HADR and Agent enabled) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
82 lines
4.1 KiB
C#
82 lines
4.1 KiB
C#
using Microsoft.Data.SqlClient;
|
|
using SQLInsight.Collectors.Instance;
|
|
using SQLInsight.Core.Data;
|
|
using SQLInsight.Core.Instances;
|
|
|
|
namespace SQLInsight.IntegrationTests;
|
|
|
|
[Collection(SqlServerTestGroup.Name)]
|
|
[Trait("Category", "Integration")]
|
|
public class HighAvailabilityTests(SqlServerFixture sql)
|
|
{
|
|
[Fact]
|
|
public async Task Availability_group_state_is_read_with_least_privilege()
|
|
{
|
|
await sql.ExecuteAsSaAsync("""
|
|
IF DB_ID(N'AgProbe') IS NULL
|
|
BEGIN
|
|
CREATE DATABASE AgProbe;
|
|
END;
|
|
""");
|
|
await sql.ExecuteAsSaAsync("""
|
|
IF NOT EXISTS (SELECT 1 FROM sys.availability_groups WHERE name = N'ag_probe')
|
|
BEGIN
|
|
ALTER DATABASE AgProbe SET RECOVERY FULL;
|
|
BACKUP DATABASE AgProbe TO DISK = N'/tmp/agprobe.bak' WITH INIT;
|
|
IF NOT EXISTS (SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
|
|
CREATE MASTER KEY ENCRYPTION BY PASSWORD = N'Mk-Pa55word!';
|
|
IF NOT EXISTS (SELECT 1 FROM sys.certificates WHERE name = N'ag_probe_cert')
|
|
CREATE CERTIFICATE ag_probe_cert WITH SUBJECT = N'ag_probe';
|
|
IF NOT EXISTS (SELECT 1 FROM sys.endpoints WHERE name = N'hadr_endpoint')
|
|
CREATE ENDPOINT hadr_endpoint STATE = STARTED AS TCP (LISTENER_PORT = 5022)
|
|
FOR DATABASE_MIRRORING (ROLE = ALL, AUTHENTICATION = CERTIFICATE ag_probe_cert, ENCRYPTION = REQUIRED ALGORITHM AES);
|
|
DECLARE @server sysname = CAST(SERVERPROPERTY('ServerName') AS sysname);
|
|
EXEC (N'CREATE AVAILABILITY GROUP ag_probe WITH (CLUSTER_TYPE = NONE) FOR DATABASE AgProbe
|
|
REPLICA ON N''' + @server + N''' WITH (ENDPOINT_URL = N''tcp://localhost:5022'', AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
|
|
FAILOVER_MODE = MANUAL, SEEDING_MODE = AUTOMATIC);');
|
|
END;
|
|
""");
|
|
|
|
await using var session = await AgentSession.OpenAsync(sql);
|
|
Assert.True(session.Capabilities.Has(Capability.HadrEnabled));
|
|
var snapshot = await session.CollectAsync<AvailabilityGroupSnapshot>(new AvailabilityGroupCollector());
|
|
|
|
var replica = Assert.Single(snapshot.Replicas, r => r.Group == "ag_probe");
|
|
Assert.True(replica.IsLocal);
|
|
Assert.Equal("PRIMARY", replica.Role);
|
|
var database = Assert.Single(snapshot.Databases, d => d.Group == "ag_probe");
|
|
Assert.Equal("AgProbe", database.Database);
|
|
Assert.True(database.IsPrimaryReplica);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Failing_agent_job_is_reported_with_the_step_message()
|
|
{
|
|
await sql.ExecuteAsSaAsync("""
|
|
IF EXISTS (SELECT 1 FROM msdb.dbo.sysjobs WHERE name = N'SQLInsight probe job')
|
|
EXEC msdb.dbo.sp_delete_job @job_name = N'SQLInsight probe job';
|
|
EXEC msdb.dbo.sp_add_job @job_name = N'SQLInsight probe job';
|
|
EXEC msdb.dbo.sp_add_jobstep @job_name = N'SQLInsight probe job', @step_name = N'Fail on purpose',
|
|
@subsystem = N'TSQL', @command = N'RAISERROR(N''sqlinsight-probe-failure'', 16, 1);';
|
|
EXEC msdb.dbo.sp_add_jobserver @job_name = N'SQLInsight probe job';
|
|
EXEC msdb.dbo.sp_start_job @job_name = N'SQLInsight probe job';
|
|
""");
|
|
|
|
await using var session = await AgentSession.OpenAsync(sql);
|
|
AgentJob? job = null;
|
|
for (var attempt = 0; attempt < 60 && job?.LastRunFailed != true; attempt++)
|
|
{
|
|
await Task.Delay(1000);
|
|
var snapshot = await session.CollectAsync<AgentJobSnapshot>(new AgentJobCollector());
|
|
Assert.True(snapshot.IsAgentRunning);
|
|
job = snapshot.Jobs.SingleOrDefault(j => j.Name == "SQLInsight probe job");
|
|
}
|
|
|
|
Assert.NotNull(job);
|
|
Assert.True(job.LastRunFailed);
|
|
Assert.Equal("Fail on purpose", job.LastFailedStepName);
|
|
Assert.Contains("sqlinsight-probe-failure", job.LastFailedStepMessage, StringComparison.Ordinal);
|
|
Assert.True(job.FailuresLastDay >= 1);
|
|
Assert.NotNull(job.LastRunAt);
|
|
}
|
|
}
|